memeloop 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/dist/browser.cjs +1413 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +3066 -0
- package/dist/browser.d.ts +3066 -0
- package/dist/browser.js +43 -0
- package/dist/chunk-2C6BJZI3.js +382 -0
- package/dist/chunk-2C6BJZI3.js.map +1 -0
- package/dist/chunk-426CJUZP.js +630 -0
- package/dist/chunk-426CJUZP.js.map +1 -0
- package/dist/chunk-7ZDTB4VZ.js +63 -0
- package/dist/chunk-7ZDTB4VZ.js.map +1 -0
- package/dist/chunk-AZFYDWIN.js +98 -0
- package/dist/chunk-AZFYDWIN.js.map +1 -0
- package/dist/chunk-BGPU4RR7.js +367 -0
- package/dist/chunk-BGPU4RR7.js.map +1 -0
- package/dist/chunk-CCEFAOSH.js +496 -0
- package/dist/chunk-CCEFAOSH.js.map +1 -0
- package/dist/chunk-FWSY5HOA.js +63 -0
- package/dist/chunk-FWSY5HOA.js.map +1 -0
- package/dist/chunk-GYIOMQMY.js +8726 -0
- package/dist/chunk-GYIOMQMY.js.map +1 -0
- package/dist/chunk-K6P63KD4.js +68 -0
- package/dist/chunk-K6P63KD4.js.map +1 -0
- package/dist/chunk-KFS2EJT6.js +42 -0
- package/dist/chunk-KFS2EJT6.js.map +1 -0
- package/dist/chunk-MAJWHAKR.js +3637 -0
- package/dist/chunk-MAJWHAKR.js.map +1 -0
- package/dist/chunk-MRKY4HGF.js +205 -0
- package/dist/chunk-MRKY4HGF.js.map +1 -0
- package/dist/chunk-T7FSXWFR.js +2744 -0
- package/dist/chunk-T7FSXWFR.js.map +1 -0
- package/dist/chunk-VZKQX5PP.js +1 -0
- package/dist/chunk-VZKQX5PP.js.map +1 -0
- package/dist/chunk-WAI6BODI.js +175 -0
- package/dist/chunk-WAI6BODI.js.map +1 -0
- package/dist/chunk-YMNXDNDW.js +798 -0
- package/dist/chunk-YMNXDNDW.js.map +1 -0
- package/dist/conversation.cjs +208 -0
- package/dist/conversation.cjs.map +1 -0
- package/dist/conversation.d.cts +100 -0
- package/dist/conversation.d.ts +100 -0
- package/dist/conversation.js +21 -0
- package/dist/conversation.js.map +1 -0
- package/dist/device-network-portable.cjs +702 -0
- package/dist/device-network-portable.cjs.map +1 -0
- package/dist/device-network-portable.d.cts +110 -0
- package/dist/device-network-portable.d.ts +110 -0
- package/dist/device-network-portable.js +53 -0
- package/dist/device-network-portable.js.map +1 -0
- package/dist/device-network.cjs +906 -0
- package/dist/device-network.cjs.map +1 -0
- package/dist/device-network.d.cts +6 -0
- package/dist/device-network.d.ts +6 -0
- package/dist/device-network.js +59 -0
- package/dist/device-network.js.map +1 -0
- package/dist/fetchProvider-WWnWnQFP.d.ts +91 -0
- package/dist/fetchProvider-toTHy5Nu.d.cts +91 -0
- package/dist/index.cjs +28051 -5459
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -1320
- package/dist/index.d.ts +94 -1320
- package/dist/index.js +10616 -2420
- package/dist/index.js.map +1 -1
- package/dist/interface-IrKBFtL1.d.cts +493 -0
- package/dist/interface-IrKBFtL1.d.ts +493 -0
- package/dist/llm-providers.cjs +43 -8
- package/dist/llm-providers.cjs.map +1 -1
- package/dist/llm-providers.d.cts +2 -1
- package/dist/llm-providers.d.ts +2 -1
- package/dist/llm-providers.js +4 -2
- package/dist/llm-providers.js.map +1 -1
- package/dist/loop-api.cjs +5227 -2010
- package/dist/loop-api.cjs.map +1 -1
- package/dist/loop-api.d.cts +11 -5
- package/dist/loop-api.d.ts +11 -5
- package/dist/loop-api.js +86 -49
- package/dist/mobile.cjs +2711 -0
- package/dist/mobile.cjs.map +1 -0
- package/dist/mobile.d.cts +6 -0
- package/dist/mobile.d.ts +6 -0
- package/dist/mobile.js +22 -0
- package/dist/mobile.js.map +1 -0
- package/dist/orchestration-portable-BHB_YrNN.d.ts +2192 -0
- package/dist/orchestration-portable-mvYBvGBb.d.cts +2192 -0
- package/dist/orchestration-portable.cjs +9553 -0
- package/dist/orchestration-portable.cjs.map +1 -0
- package/dist/orchestration-portable.d.cts +5 -0
- package/dist/orchestration-portable.d.ts +5 -0
- package/dist/orchestration-portable.js +290 -0
- package/dist/orchestration-portable.js.map +1 -0
- package/dist/pairingInvite-BXPWY3aM.d.cts +149 -0
- package/dist/pairingInvite-Cuz0wuq9.d.ts +149 -0
- package/dist/promptConcat-0WNFTg4l.d.ts +516 -0
- package/dist/promptConcat-D6wr5V-V.d.cts +516 -0
- package/dist/providerRegistry-Bi6pAvSj.d.ts +86 -0
- package/dist/providerRegistry-CPsktuiX.d.cts +86 -0
- package/dist/registry-D0kbxksn.d.cts +393 -0
- package/dist/registry-DDbvhlhs.d.ts +393 -0
- package/dist/remoteClient-B_spK019.d.ts +87 -0
- package/dist/remoteClient-CBggnRy3.d.cts +87 -0
- package/dist/runtime-PGWR6FIV.js +18 -0
- package/dist/runtime-PGWR6FIV.js.map +1 -0
- package/dist/scriptDeploymentPipeline-CXJcC82H.d.ts +2464 -0
- package/dist/scriptDeploymentPipeline-yG1ZhVVI.d.cts +2464 -0
- package/dist/syncCloudDevices-BEqwgrhD.d.ts +49 -0
- package/dist/syncCloudDevices-Ds_Kcgrv.d.cts +49 -0
- package/dist/types-7pYti-k9.d.cts +255 -0
- package/dist/types-CdFVSntj.d.ts +255 -0
- package/package.json +110 -35
- package/dist/anthropic.cjs +0 -72
- package/dist/anthropic.cjs.map +0 -1
- package/dist/anthropic.d.cts +0 -21
- package/dist/anthropic.d.ts +0 -21
- package/dist/anthropic.js +0 -21
- package/dist/anthropic.js.map +0 -1
- package/dist/azure.cjs +0 -72
- package/dist/azure.cjs.map +0 -1
- package/dist/azure.d.cts +0 -21
- package/dist/azure.d.ts +0 -21
- package/dist/azure.js +0 -21
- package/dist/azure.js.map +0 -1
- package/dist/chunk-3CN7DFBL.js +0 -4894
- package/dist/chunk-3CN7DFBL.js.map +0 -1
- package/dist/chunk-4AZ4D7U5.js +0 -32
- package/dist/chunk-4AZ4D7U5.js.map +0 -1
- package/dist/chunk-6ON4XSOV.js +0 -63
- package/dist/chunk-6ON4XSOV.js.map +0 -1
- package/dist/chunk-DJQNGIXX.js +0 -4793
- package/dist/chunk-DJQNGIXX.js.map +0 -1
- package/dist/chunk-FKQP6AOQ.js +0 -32
- package/dist/chunk-FKQP6AOQ.js.map +0 -1
- package/dist/chunk-OVJ7JJBA.js +0 -35
- package/dist/chunk-OVJ7JJBA.js.map +0 -1
- package/dist/chunk-P4UZ7IXC.js +0 -42
- package/dist/chunk-YHJ2AEFR.js +0 -4933
- package/dist/chunk-YHJ2AEFR.js.map +0 -1
- package/dist/chunk-YVK7RYRO.js +0 -4973
- package/dist/chunk-YVK7RYRO.js.map +0 -1
- package/dist/code-assistant.json +0 -114
- package/dist/cohere.cjs +0 -72
- package/dist/cohere.cjs.map +0 -1
- package/dist/cohere.d.cts +0 -21
- package/dist/cohere.d.ts +0 -21
- package/dist/cohere.js +0 -21
- package/dist/cohere.js.map +0 -1
- package/dist/deepseek.cjs +0 -72
- package/dist/deepseek.cjs.map +0 -1
- package/dist/deepseek.d.cts +0 -21
- package/dist/deepseek.d.ts +0 -21
- package/dist/deepseek.js +0 -21
- package/dist/deepseek.js.map +0 -1
- package/dist/dist-65U7OYRG.js +0 -1257
- package/dist/dist-65U7OYRG.js.map +0 -1
- package/dist/fetchProvider-Cu4WkU5O.d.cts +0 -819
- package/dist/fetchProvider-Cu4WkU5O.d.ts +0 -819
- package/dist/frontend-ui-ux.json +0 -105
- package/dist/general-assistant.json +0 -95
- package/dist/git-master.json +0 -51
- package/dist/google-vertex.cjs +0 -72
- package/dist/google-vertex.cjs.map +0 -1
- package/dist/google-vertex.d.cts +0 -21
- package/dist/google-vertex.d.ts +0 -21
- package/dist/google-vertex.js +0 -21
- package/dist/google-vertex.js.map +0 -1
- package/dist/google.cjs +0 -72
- package/dist/google.cjs.map +0 -1
- package/dist/google.d.cts +0 -21
- package/dist/google.d.ts +0 -21
- package/dist/google.js +0 -21
- package/dist/google.js.map +0 -1
- package/dist/groq.cjs +0 -72
- package/dist/groq.cjs.map +0 -1
- package/dist/groq.d.cts +0 -21
- package/dist/groq.d.ts +0 -21
- package/dist/groq.js +0 -21
- package/dist/groq.js.map +0 -1
- package/dist/index.d.mts +0 -3203
- package/dist/index.mjs +0 -7064
- package/dist/index.mjs.map +0 -1
- package/dist/mistral.cjs +0 -72
- package/dist/mistral.cjs.map +0 -1
- package/dist/mistral.d.cts +0 -21
- package/dist/mistral.d.ts +0 -21
- package/dist/mistral.js +0 -21
- package/dist/mistral.js.map +0 -1
- package/dist/multipart-parser-EJCXIUHC.js +0 -372
- package/dist/multipart-parser-EJCXIUHC.js.map +0 -1
- package/dist/openai.cjs +0 -72
- package/dist/openai.cjs.map +0 -1
- package/dist/openai.d.cts +0 -21
- package/dist/openai.d.ts +0 -21
- package/dist/openai.js +0 -21
- package/dist/openai.js.map +0 -1
- package/dist/perplexity.cjs +0 -72
- package/dist/perplexity.cjs.map +0 -1
- package/dist/perplexity.d.cts +0 -21
- package/dist/perplexity.d.ts +0 -21
- package/dist/perplexity.js +0 -21
- package/dist/perplexity.js.map +0 -1
- package/dist/playwright.json +0 -52
- package/dist/promptConcat-BJdazTg5.d.cts +0 -1832
- package/dist/promptConcat-BJdazTg5.d.ts +0 -1832
- package/dist/promptConcat-CAkpni_8.d.cts +0 -1834
- package/dist/promptConcat-CAkpni_8.d.ts +0 -1834
- package/dist/promptConcat-CfH5-GAE.d.cts +0 -1036
- package/dist/promptConcat-ChcAZOOL.d.ts +0 -1036
- package/dist/promptConcat-G5TnJVRy.d.cts +0 -1800
- package/dist/promptConcat-G5TnJVRy.d.ts +0 -1800
- package/dist/promptConcat-L72eowzp.d.ts +0 -1090
- package/dist/promptConcat-eLHdN7K8.d.cts +0 -1090
- package/dist/src-VTSXKG72.js +0 -1409
- package/dist/src-VTSXKG72.js.map +0 -1
- package/dist/togetherai.cjs +0 -72
- package/dist/togetherai.cjs.map +0 -1
- package/dist/togetherai.d.cts +0 -21
- package/dist/togetherai.d.ts +0 -21
- package/dist/togetherai.js +0 -21
- package/dist/togetherai.js.map +0 -1
- package/dist/types-B0Jm2l38.d.cts +0 -767
- package/dist/types-B0Jm2l38.d.ts +0 -767
- package/dist/xai.cjs +0 -72
- package/dist/xai.cjs.map +0 -1
- package/dist/xai.d.cts +0 -21
- package/dist/xai.d.ts +0 -21
- package/dist/xai.js +0 -21
- package/dist/xai.js.map +0 -1
- /package/dist/{chunk-P4UZ7IXC.js.map → browser.js.map} +0 -0
package/dist/chunk-3CN7DFBL.js
DELETED
|
@@ -1,4894 +0,0 @@
|
|
|
1
|
-
// src/loops/agent-agent-loop/builtinLoopSources.ts
|
|
2
|
-
var AgentAgentLoopQualityGateScriptId = "builtin:agent-agent-loop/quality-gate";
|
|
3
|
-
var BUILTIN_AGENT_AGENT_LOOP_QUALITY_GATE_SCRIPT_ID = AgentAgentLoopQualityGateScriptId;
|
|
4
|
-
var builtinAgentAgentLoopSources = {
|
|
5
|
-
[AgentAgentLoopQualityGateScriptId]: [
|
|
6
|
-
"// @ts-check",
|
|
7
|
-
"",
|
|
8
|
-
"function positiveInteger(value, fallback) {",
|
|
9
|
-
" const number = Number(value);",
|
|
10
|
-
" return Number.isFinite(number) && number > 0 ? Math.floor(number) : fallback;",
|
|
11
|
-
"}",
|
|
12
|
-
"",
|
|
13
|
-
"function asEntries(value, fallback) {",
|
|
14
|
-
" return Array.isArray(value) && value.length > 0 ? value : fallback;",
|
|
15
|
-
"}",
|
|
16
|
-
"",
|
|
17
|
-
"function withConversationIds(ctx, entries, phase, iteration) {",
|
|
18
|
-
" return entries.map((entry, index) => {",
|
|
19
|
-
" const record = typeof entry === 'string' ? { profile: entry } : entry;",
|
|
20
|
-
" return {",
|
|
21
|
-
" ...record,",
|
|
22
|
-
" conversationId: record.conversationId ?? `${ctx.input.conversationId}:${phase}:${iteration}:${index}`,",
|
|
23
|
-
" };",
|
|
24
|
-
" });",
|
|
25
|
-
"}",
|
|
26
|
-
"",
|
|
27
|
-
"function resultText(ctx, result) {",
|
|
28
|
-
" return result.text || ctx.formatAgentResults(result, { includeFailureSection: true });",
|
|
29
|
-
"}",
|
|
30
|
-
"",
|
|
31
|
-
"function reviewText(ctx, result) {",
|
|
32
|
-
" return ctx.formatAgentResults(result, { includeFailureSection: true });",
|
|
33
|
-
"}",
|
|
34
|
-
"",
|
|
35
|
-
"function approved(result) {",
|
|
36
|
-
" return result.failures.length === 0 &&",
|
|
37
|
-
" result.results.length > 0 &&",
|
|
38
|
-
" result.results.every(item => /^\\s*APPROVED\\b/imu.test(item.text));",
|
|
39
|
-
"}",
|
|
40
|
-
"",
|
|
41
|
-
"function feedbackBlock(ctx, history) {",
|
|
42
|
-
" if (history.length === 0) return 'No prior review feedback.';",
|
|
43
|
-
" return history.map(entry => [",
|
|
44
|
-
" `Iteration ${entry.iteration}`,",
|
|
45
|
-
" `Attempt:\\n${resultText(ctx, entry.attempt)}`,",
|
|
46
|
-
" `Review:\\n${reviewText(ctx, entry.review)}`,",
|
|
47
|
-
" ].join('\\n\\n')).join('\\n\\n---\\n\\n');",
|
|
48
|
-
"}",
|
|
49
|
-
"",
|
|
50
|
-
"function workerPrompt(ctx, goal, iteration, previousAttempt, history) {",
|
|
51
|
-
" if (!previousAttempt) return goal;",
|
|
52
|
-
" return [",
|
|
53
|
-
" 'Continue working on the original goal until the reviewer can approve it.',",
|
|
54
|
-
" 'Do not summarize the review. Produce the corrected deliverable itself.',",
|
|
55
|
-
" `Original goal:\\n${goal}`,",
|
|
56
|
-
" `Previous deliverable:\\n${resultText(ctx, previousAttempt)}`,",
|
|
57
|
-
" `Review history:\\n${feedbackBlock(ctx, history)}`,",
|
|
58
|
-
" `This is revision attempt ${iteration}.`,",
|
|
59
|
-
" ].join('\\n\\n');",
|
|
60
|
-
"}",
|
|
61
|
-
"",
|
|
62
|
-
"function reviewPrompt(ctx, goal, attempt, history) {",
|
|
63
|
-
" return [",
|
|
64
|
-
" 'You are the quality gate for this delegated agent run.',",
|
|
65
|
-
" 'Return APPROVED on the first line only when the deliverable fully satisfies the original goal and is ready to send to the user.',",
|
|
66
|
-
" 'Return REVISE on the first line when more work is required, followed by concrete blocking issues and exact changes needed.',",
|
|
67
|
-
" 'Do not approve partial progress.',",
|
|
68
|
-
" `Original goal:\\n${goal}`,",
|
|
69
|
-
" `Candidate deliverable:\\n${resultText(ctx, attempt)}`,",
|
|
70
|
-
" `Earlier review history:\\n${feedbackBlock(ctx, history)}`,",
|
|
71
|
-
" ].join('\\n\\n');",
|
|
72
|
-
"}",
|
|
73
|
-
"",
|
|
74
|
-
"async function runBatch(ctx, mode, input) {",
|
|
75
|
-
" return mode === 'sequential' ? ctx.runSequential(input) : ctx.runParallel(input);",
|
|
76
|
-
"}",
|
|
77
|
-
"",
|
|
78
|
-
"/** @param {import('../../loopAPI/agent-agent-loop/loop.js').AgentAgentScriptContext} ctx */",
|
|
79
|
-
"export default async function run(ctx) {",
|
|
80
|
-
" const metadata = ctx.profile?.metadata ?? {};",
|
|
81
|
-
" const goal = ctx.input.message;",
|
|
82
|
-
" const maxIterations = positiveInteger(metadata.maxIterations, 4);",
|
|
83
|
-
" const mode = metadata.mode === 'sequential' ? 'sequential' : 'parallel';",
|
|
84
|
-
" const reviewMode = metadata.reviewMode === 'sequential' ? 'sequential' : 'parallel';",
|
|
85
|
-
" const defaultAgents = ctx.getAgentEntries('agents');",
|
|
86
|
-
" const workers = asEntries(ctx.getAgentEntries('workers'), asEntries(ctx.getAgentEntries('draftAgents'), defaultAgents));",
|
|
87
|
-
" const reviewers = asEntries(ctx.getAgentEntries('reviewers'), asEntries(ctx.getAgentEntries('reviewAgents'), defaultAgents));",
|
|
88
|
-
" const fixers = asEntries(ctx.getAgentEntries('fixers'), asEntries(ctx.getAgentEntries('revisionAgents'), workers));",
|
|
89
|
-
" const history = [];",
|
|
90
|
-
" let attempt;",
|
|
91
|
-
"",
|
|
92
|
-
" if (workers.length === 0 || reviewers.length === 0) {",
|
|
93
|
-
" ctx.finish('AgentAgent quality gate requires worker and reviewer agents. Configure metadata.workers/reviewers or metadata.agents.');",
|
|
94
|
-
" return;",
|
|
95
|
-
" }",
|
|
96
|
-
"",
|
|
97
|
-
" for (let iteration = 1; iteration <= maxIterations; iteration += 1) {",
|
|
98
|
-
" if (ctx.isCancelled()) break;",
|
|
99
|
-
" const activeWorkers = iteration === 1 ? workers : fixers;",
|
|
100
|
-
" ctx.emit({",
|
|
101
|
-
" type: 'thinking',",
|
|
102
|
-
" data: { status: 'quality-gate-work', conversationId: ctx.input.conversationId, iteration },",
|
|
103
|
-
" });",
|
|
104
|
-
" attempt = await runBatch(ctx, mode, {",
|
|
105
|
-
" agents: withConversationIds(ctx, activeWorkers, 'work', iteration),",
|
|
106
|
-
" prompt: workerPrompt(ctx, goal, iteration, attempt, history),",
|
|
107
|
-
" });",
|
|
108
|
-
" await ctx.checkpoint(`quality-gate:${iteration}:attempt`, attempt);",
|
|
109
|
-
"",
|
|
110
|
-
" if (attempt.failures.length > 0 && attempt.results.length === 0) {",
|
|
111
|
-
" history.push({ iteration, attempt, review: { results: [], failures: attempt.failures, text: '' } });",
|
|
112
|
-
" continue;",
|
|
113
|
-
" }",
|
|
114
|
-
"",
|
|
115
|
-
" ctx.emit({",
|
|
116
|
-
" type: 'thinking',",
|
|
117
|
-
" data: { status: 'quality-gate-review', conversationId: ctx.input.conversationId, iteration },",
|
|
118
|
-
" });",
|
|
119
|
-
" const review = await runBatch(ctx, reviewMode, {",
|
|
120
|
-
" agents: withConversationIds(ctx, reviewers, 'review', iteration),",
|
|
121
|
-
" prompt: reviewPrompt(ctx, goal, attempt, history),",
|
|
122
|
-
" });",
|
|
123
|
-
" history.push({ iteration, attempt, review });",
|
|
124
|
-
" await ctx.checkpoint(`quality-gate:${iteration}:review`, review);",
|
|
125
|
-
"",
|
|
126
|
-
" if (approved(review)) {",
|
|
127
|
-
" ctx.finish(resultText(ctx, attempt));",
|
|
128
|
-
" return;",
|
|
129
|
-
" }",
|
|
130
|
-
" }",
|
|
131
|
-
"",
|
|
132
|
-
" ctx.finish([",
|
|
133
|
-
" 'Quality gate did not approve the delegated work before the iteration limit.',",
|
|
134
|
-
" attempt ? resultText(ctx, attempt) : 'No deliverable was produced.',",
|
|
135
|
-
" 'Review trail:',",
|
|
136
|
-
" feedbackBlock(ctx, history),",
|
|
137
|
-
" ].filter(Boolean).join('\\n\\n'));",
|
|
138
|
-
"}",
|
|
139
|
-
""
|
|
140
|
-
].join("\n")
|
|
141
|
-
};
|
|
142
|
-
function getBuiltinAgentAgentLoopSource(id) {
|
|
143
|
-
return builtinAgentAgentLoopSources[id];
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// src/loopAPI/scriptLoader.ts
|
|
147
|
-
var DEFAULT_POLICY = {
|
|
148
|
-
allowBuiltin: true,
|
|
149
|
-
allowFile: false,
|
|
150
|
-
allowNetwork: false,
|
|
151
|
-
allowSource: false,
|
|
152
|
-
allowSpecifier: true
|
|
153
|
-
};
|
|
154
|
-
function getExportedScript(moduleExports) {
|
|
155
|
-
if (typeof moduleExports === "function") return moduleExports;
|
|
156
|
-
if (!moduleExports || typeof moduleExports !== "object") return void 0;
|
|
157
|
-
const record = moduleExports;
|
|
158
|
-
return record.default ?? record.run;
|
|
159
|
-
}
|
|
160
|
-
function sourceToDataSpecifier(source, name = "agent-loop-script.mjs") {
|
|
161
|
-
return `data:text/javascript;charset=utf-8,${encodeURIComponent(`${source}
|
|
162
|
-
//# sourceURL=${name}`)}`;
|
|
163
|
-
}
|
|
164
|
-
function normalizeScriptReference(scriptReference) {
|
|
165
|
-
if (typeof scriptReference !== "string") return scriptReference;
|
|
166
|
-
if (scriptReference.startsWith("builtin:")) return { kind: "builtin", id: scriptReference };
|
|
167
|
-
return { kind: "specifier", specifier: scriptReference };
|
|
168
|
-
}
|
|
169
|
-
function isFileSpecifier(specifier) {
|
|
170
|
-
return specifier.startsWith("file:") || specifier.startsWith("/") || specifier.startsWith("./") || specifier.startsWith("../");
|
|
171
|
-
}
|
|
172
|
-
function isNetworkSpecifier(specifier) {
|
|
173
|
-
return specifier.startsWith("http:") || specifier.startsWith("https:");
|
|
174
|
-
}
|
|
175
|
-
function isSourceSpecifier(specifier) {
|
|
176
|
-
return specifier.startsWith("data:text/javascript") || specifier.startsWith("data:application/javascript");
|
|
177
|
-
}
|
|
178
|
-
function assertSpecifierAllowed(specifier, policy) {
|
|
179
|
-
if (!policy.allowSpecifier) {
|
|
180
|
-
throw new Error("Agent loop script import specifiers are disabled by policy.");
|
|
181
|
-
}
|
|
182
|
-
if (isSourceSpecifier(specifier) && !policy.allowSource) {
|
|
183
|
-
throw new Error("Agent loop script source/data URLs are disabled by policy.");
|
|
184
|
-
}
|
|
185
|
-
if (isFileSpecifier(specifier) && !policy.allowFile) {
|
|
186
|
-
throw new Error("Agent loop script file imports are disabled by policy.");
|
|
187
|
-
}
|
|
188
|
-
if (isNetworkSpecifier(specifier) && !policy.allowNetwork) {
|
|
189
|
-
throw new Error("Agent loop script network imports are disabled by policy.");
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
async function loadAgentLoopScript(scriptReference, options = {}) {
|
|
193
|
-
const policy = { ...DEFAULT_POLICY, ...options };
|
|
194
|
-
const importModule = options.importModule ?? ((specifier) => import(specifier));
|
|
195
|
-
const normalized = normalizeScriptReference(scriptReference);
|
|
196
|
-
const scriptType = options.scriptType ?? "Agent loop script";
|
|
197
|
-
let scriptSpecifier;
|
|
198
|
-
let isBuiltinScript = false;
|
|
199
|
-
if (normalized.kind === "builtin") {
|
|
200
|
-
if (!policy.allowBuiltin) {
|
|
201
|
-
throw new Error("Built-in agent loop scripts are disabled by policy.");
|
|
202
|
-
}
|
|
203
|
-
const source = options.getBuiltinScriptSource?.(normalized.id);
|
|
204
|
-
if (!source) throw new Error(`Unknown built-in agent loop script: ${normalized.id}`);
|
|
205
|
-
scriptSpecifier = sourceToDataSpecifier(source, normalized.id);
|
|
206
|
-
isBuiltinScript = true;
|
|
207
|
-
} else if (normalized.kind === "source") {
|
|
208
|
-
if (!policy.allowSource) {
|
|
209
|
-
throw new Error("Agent loop script source strings are disabled by policy.");
|
|
210
|
-
}
|
|
211
|
-
scriptSpecifier = sourceToDataSpecifier(normalized.source, normalized.name);
|
|
212
|
-
} else {
|
|
213
|
-
scriptSpecifier = normalized.specifier;
|
|
214
|
-
}
|
|
215
|
-
if (!isBuiltinScript) {
|
|
216
|
-
assertSpecifierAllowed(scriptSpecifier, policy);
|
|
217
|
-
}
|
|
218
|
-
const moduleExports = await importModule(scriptSpecifier);
|
|
219
|
-
const exportedScript = getExportedScript(moduleExports);
|
|
220
|
-
if (typeof exportedScript !== "function") {
|
|
221
|
-
throw new TypeError(
|
|
222
|
-
`${scriptType} "${scriptSpecifier}" must export a default function or named run function.`
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
return exportedScript;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// src/loopAPI/agent-agent-loop/scriptLoader.ts
|
|
229
|
-
async function loadAgentAgentLoopScript(scriptReference, options = {}) {
|
|
230
|
-
return loadAgentLoopScript(scriptReference, {
|
|
231
|
-
...options,
|
|
232
|
-
getBuiltinScriptSource: getBuiltinAgentAgentLoopSource,
|
|
233
|
-
scriptType: "AgentAgentLoop script"
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// src/loopAPI/agent-agent-loop/loop.ts
|
|
238
|
-
var LOOP_ID = "agent-agent-loop";
|
|
239
|
-
var LOOP_NAME = "AgentAgent Loop";
|
|
240
|
-
var LOOP_DESC = "Orchestrates child agents to collaborate on a task. Supports sequential chaining, parallel execution, and feedback loops.";
|
|
241
|
-
function isAsyncIterable(value) {
|
|
242
|
-
return Boolean(value && typeof value === "object" && Symbol.asyncIterator in value);
|
|
243
|
-
}
|
|
244
|
-
function messageStep(message) {
|
|
245
|
-
return { type: "message", data: message };
|
|
246
|
-
}
|
|
247
|
-
function stepText(step) {
|
|
248
|
-
if (step.type !== "message") return void 0;
|
|
249
|
-
if (typeof step.data === "string") return step.data;
|
|
250
|
-
if (step.data && typeof step.data === "object" && "content" in step.data) {
|
|
251
|
-
const content = step.data.content;
|
|
252
|
-
return typeof content === "string" ? content : void 0;
|
|
253
|
-
}
|
|
254
|
-
return void 0;
|
|
255
|
-
}
|
|
256
|
-
function profileIdFromEntry(entry) {
|
|
257
|
-
if (typeof entry === "string") return entry;
|
|
258
|
-
return entry.profileId ?? entry.profile;
|
|
259
|
-
}
|
|
260
|
-
function readProfileAgentEntries(profile, key = "agents") {
|
|
261
|
-
const metadata = profile?.metadata;
|
|
262
|
-
if (!metadata) return void 0;
|
|
263
|
-
const entries = metadata[key];
|
|
264
|
-
if (Array.isArray(entries)) return entries;
|
|
265
|
-
return void 0;
|
|
266
|
-
}
|
|
267
|
-
function normalizeAgentAgents(input, context, entries, prompt) {
|
|
268
|
-
const configuredEntries = entries ?? context.agents ?? readProfileAgentEntries(context.profile) ?? [];
|
|
269
|
-
return configuredEntries.flatMap((entry, index) => {
|
|
270
|
-
const profileId = profileIdFromEntry(entry);
|
|
271
|
-
if (!profileId) return [];
|
|
272
|
-
const record = typeof entry === "string" ? {} : entry;
|
|
273
|
-
return {
|
|
274
|
-
...record,
|
|
275
|
-
profileId,
|
|
276
|
-
prompt: prompt ?? record.prompt,
|
|
277
|
-
conversationId: record.conversationId ?? `${input.conversationId}:child:${index}`,
|
|
278
|
-
label: record.label
|
|
279
|
-
};
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
function formatAgentResults(result, options = {}) {
|
|
283
|
-
const emptyMessage = options.emptyMessage ?? "No child agent output.";
|
|
284
|
-
const failureHeader = options.failureHeader ?? "Failed child agents:";
|
|
285
|
-
const includeFailureSection = options.includeFailureSection !== false;
|
|
286
|
-
const sections = [result.text || emptyMessage];
|
|
287
|
-
if (includeFailureSection && result.failures.length > 0) {
|
|
288
|
-
sections.push(`${failureHeader}
|
|
289
|
-
${result.failures.map((failure) => `${failure.profileId}: ${failure.error}`).join("\n")}`);
|
|
290
|
-
}
|
|
291
|
-
return sections.filter(Boolean).join("\n\n");
|
|
292
|
-
}
|
|
293
|
-
function createScriptArguments(input, context, emittedSteps) {
|
|
294
|
-
const agentEntries = context.agents ?? readProfileAgentEntries(context.profile) ?? [];
|
|
295
|
-
const agents = normalizeAgentAgents(input, context, agentEntries);
|
|
296
|
-
const getAgentEntries = (key = "agents") => {
|
|
297
|
-
if (key === "agents") return agentEntries;
|
|
298
|
-
return readProfileAgentEntries(context.profile, key) ?? [];
|
|
299
|
-
};
|
|
300
|
-
const emit = (step) => {
|
|
301
|
-
emittedSteps.push(step);
|
|
302
|
-
context.runtime?.emit?.(step);
|
|
303
|
-
};
|
|
304
|
-
const log = (event, data) => {
|
|
305
|
-
context.runtime?.log?.(event, data);
|
|
306
|
-
};
|
|
307
|
-
const isCancelled = () => context.runtime?.signal?.cancelled === true;
|
|
308
|
-
const runAgent = async (childInput) => {
|
|
309
|
-
if (isCancelled()) throw new Error("AgentAgent_Loop cancelled before child agent start");
|
|
310
|
-
const profileId = childInput.profileId ?? childInput.profile;
|
|
311
|
-
if (!profileId) throw new Error("ctx.runAgent requires profileId or profile");
|
|
312
|
-
if (!context.runtime?.runChildAgent) throw new Error("ctx.runAgent requires runtime.runChildAgent");
|
|
313
|
-
const childConversationId = childInput.conversationId ?? `${input.conversationId}:child:${profileId}:${Date.now().toString(36)}`;
|
|
314
|
-
const prompt = childInput.prompt ?? input.message;
|
|
315
|
-
emit({
|
|
316
|
-
type: "thinking",
|
|
317
|
-
data: { status: "child-agent-started", profileId, conversationId: childConversationId }
|
|
318
|
-
});
|
|
319
|
-
const steps = [];
|
|
320
|
-
const chunks = [];
|
|
321
|
-
try {
|
|
322
|
-
for await (const step of context.runtime.runChildAgent({
|
|
323
|
-
profileId,
|
|
324
|
-
prompt,
|
|
325
|
-
conversationId: childConversationId
|
|
326
|
-
})) {
|
|
327
|
-
if (isCancelled()) throw new Error("AgentAgent_Loop cancelled during child agent run");
|
|
328
|
-
steps.push(step);
|
|
329
|
-
const text = stepText(step);
|
|
330
|
-
if (text) chunks.push(text);
|
|
331
|
-
emit({
|
|
332
|
-
type: "thinking",
|
|
333
|
-
data: { status: "child-agent-step", profileId, conversationId: childConversationId, step }
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
} catch (error) {
|
|
337
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
338
|
-
emit({
|
|
339
|
-
type: "thinking",
|
|
340
|
-
data: { status: "child-agent-failed", profileId, conversationId: childConversationId, error: message }
|
|
341
|
-
});
|
|
342
|
-
throw error;
|
|
343
|
-
}
|
|
344
|
-
emit({
|
|
345
|
-
type: "thinking",
|
|
346
|
-
data: { status: "child-agent-completed", profileId, conversationId: childConversationId }
|
|
347
|
-
});
|
|
348
|
-
return { profileId, conversationId: childConversationId, steps, text: chunks.join("").trim() };
|
|
349
|
-
};
|
|
350
|
-
const runBatch = async (batchInput, mode) => {
|
|
351
|
-
const batchAgents = normalizeAgentAgents(input, context, batchInput?.agents, batchInput?.prompt);
|
|
352
|
-
const continueOnError = batchInput?.continueOnError !== false;
|
|
353
|
-
const results = [];
|
|
354
|
-
const failures = [];
|
|
355
|
-
const runOne = async (agent) => {
|
|
356
|
-
if (isCancelled()) {
|
|
357
|
-
emit({
|
|
358
|
-
type: "thinking",
|
|
359
|
-
data: { status: "cancelled", conversationId: input.conversationId }
|
|
360
|
-
});
|
|
361
|
-
return;
|
|
362
|
-
}
|
|
363
|
-
try {
|
|
364
|
-
const result = await runAgent(agent);
|
|
365
|
-
results.push(result);
|
|
366
|
-
} catch (error) {
|
|
367
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
368
|
-
failures.push({ profileId: agent.profileId, conversationId: agent.conversationId, error: message });
|
|
369
|
-
if (!continueOnError) throw error;
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
if (mode === "parallel") {
|
|
373
|
-
await Promise.all(batchAgents.map((agent) => runOne(agent)));
|
|
374
|
-
} else {
|
|
375
|
-
for (const agent of batchAgents) {
|
|
376
|
-
await runOne(agent);
|
|
377
|
-
if (isCancelled()) break;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return { results, failures, text: results.map((result) => result.text).filter(Boolean).join("\n\n") };
|
|
381
|
-
};
|
|
382
|
-
return {
|
|
383
|
-
input,
|
|
384
|
-
context,
|
|
385
|
-
profile: context.profile,
|
|
386
|
-
agents,
|
|
387
|
-
getAgentEntries,
|
|
388
|
-
runtime: context.runtime,
|
|
389
|
-
runAgent,
|
|
390
|
-
runAgents: (inputs) => Promise.all(inputs.map(runAgent)),
|
|
391
|
-
runSequential: (batchInput) => runBatch(batchInput, "sequential"),
|
|
392
|
-
runParallel: (batchInput) => runBatch(batchInput, "parallel"),
|
|
393
|
-
formatAgentResults,
|
|
394
|
-
finishAgentResults: (result, options) => {
|
|
395
|
-
emit(messageStep(formatAgentResults(result, options)));
|
|
396
|
-
},
|
|
397
|
-
emit,
|
|
398
|
-
finish: (message) => {
|
|
399
|
-
emit(typeof message === "string" ? messageStep(message) : message);
|
|
400
|
-
},
|
|
401
|
-
isCancelled,
|
|
402
|
-
log,
|
|
403
|
-
state: context.runtime?.state ?? {
|
|
404
|
-
get: async () => void 0,
|
|
405
|
-
set: async () => void 0,
|
|
406
|
-
update: async () => void 0
|
|
407
|
-
},
|
|
408
|
-
checkpoint: context.runtime?.checkpoint ?? (async () => void 0)
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
async function* drainEmittedSteps(steps) {
|
|
412
|
-
while (steps.length > 0) {
|
|
413
|
-
const step = steps.shift();
|
|
414
|
-
if (step) yield step;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
async function* runScript(script, input, context) {
|
|
418
|
-
const emittedSteps = [];
|
|
419
|
-
const scriptArguments = createScriptArguments(input, context, emittedSteps);
|
|
420
|
-
const result = await script(scriptArguments);
|
|
421
|
-
yield* drainEmittedSteps(emittedSteps);
|
|
422
|
-
if (isAsyncIterable(result)) {
|
|
423
|
-
yield* result;
|
|
424
|
-
yield* drainEmittedSteps(emittedSteps);
|
|
425
|
-
} else if (typeof result === "string") {
|
|
426
|
-
yield messageStep(result);
|
|
427
|
-
} else if (Array.isArray(result)) {
|
|
428
|
-
yield* result;
|
|
429
|
-
} else if (result) {
|
|
430
|
-
yield result;
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
function asAgentAgentContext(context) {
|
|
434
|
-
return context;
|
|
435
|
-
}
|
|
436
|
-
async function resolveScript(context) {
|
|
437
|
-
if (context.script) return context.script;
|
|
438
|
-
const scriptReference = context.profile?.scriptReference ?? context.profile?.scriptRef ?? context.profile?.script;
|
|
439
|
-
if (scriptReference) {
|
|
440
|
-
if (context.loadScript) return context.loadScript(scriptReference, context);
|
|
441
|
-
return loadAgentAgentLoopScript(scriptReference, context.scriptPolicy);
|
|
442
|
-
}
|
|
443
|
-
return void 0;
|
|
444
|
-
}
|
|
445
|
-
function createAgentAgentLoopDefinition() {
|
|
446
|
-
return {
|
|
447
|
-
id: LOOP_ID,
|
|
448
|
-
name: LOOP_NAME,
|
|
449
|
-
description: LOOP_DESC,
|
|
450
|
-
createRunner: (rawContext) => {
|
|
451
|
-
return async function* agentAgentLoop(input) {
|
|
452
|
-
const context = asAgentAgentContext(rawContext);
|
|
453
|
-
const script = await resolveScript(context);
|
|
454
|
-
yield {
|
|
455
|
-
type: "thinking",
|
|
456
|
-
data: { status: "agent-agent-loop-loop-started", conversationId: input.conversationId }
|
|
457
|
-
};
|
|
458
|
-
context.runtime?.log?.("agent-agent-loop-loop-started", {
|
|
459
|
-
conversationId: input.conversationId,
|
|
460
|
-
profileId: context.profile?.id
|
|
461
|
-
});
|
|
462
|
-
if (script) {
|
|
463
|
-
yield* runScript(script, input, context);
|
|
464
|
-
yield {
|
|
465
|
-
type: "thinking",
|
|
466
|
-
data: { status: "completed", conversationId: input.conversationId }
|
|
467
|
-
};
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
yield {
|
|
471
|
-
type: "thinking",
|
|
472
|
-
data: { status: "script-missing", conversationId: input.conversationId }
|
|
473
|
-
};
|
|
474
|
-
yield {
|
|
475
|
-
type: "message",
|
|
476
|
-
data: "AgentAgentLoop requires an explicit loop script. Configure profile.scriptReference or provide context.script."
|
|
477
|
-
};
|
|
478
|
-
yield {
|
|
479
|
-
type: "thinking",
|
|
480
|
-
data: { status: "completed", conversationId: input.conversationId }
|
|
481
|
-
};
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
function getAgentAgentLoopId() {
|
|
487
|
-
return LOOP_ID;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// src/loops/agent-tool-loop/builtinLoopSources.ts
|
|
491
|
-
var AgentToolLoopDefaultScriptId = "builtin:agent-tool-loop/default";
|
|
492
|
-
var BUILTIN_AGENT_TOOL_LOOP_DEFAULT_SCRIPT_ID = AgentToolLoopDefaultScriptId;
|
|
493
|
-
var builtinAgentToolLoopSources = {
|
|
494
|
-
[AgentToolLoopDefaultScriptId]: [
|
|
495
|
-
"// @ts-check",
|
|
496
|
-
"",
|
|
497
|
-
"/** @param {import('../../loopAPI/agent-tool-loop/loop.js').AgentToolLoopScriptContext} ctx */",
|
|
498
|
-
"export default async function* run(ctx) {",
|
|
499
|
-
" const state = ctx.createState();",
|
|
500
|
-
"",
|
|
501
|
-
" try {",
|
|
502
|
-
" const start = await ctx.startTurn(state);",
|
|
503
|
-
" if (start.step) yield start.step;",
|
|
504
|
-
" if (start.action === 'stop') return;",
|
|
505
|
-
"",
|
|
506
|
-
" while (true) {",
|
|
507
|
-
" const result = yield* ctx.runIteration(state);",
|
|
508
|
-
" if (result.action === 'stop') return;",
|
|
509
|
-
" }",
|
|
510
|
-
" } catch (error) {",
|
|
511
|
-
" await ctx.stopTurn(state, 'error');",
|
|
512
|
-
" throw error;",
|
|
513
|
-
" } finally {",
|
|
514
|
-
" await ctx.stopTurn(state);",
|
|
515
|
-
" }",
|
|
516
|
-
"}"
|
|
517
|
-
].join("\n")
|
|
518
|
-
};
|
|
519
|
-
function getBuiltinAgentToolLoopSource(id) {
|
|
520
|
-
return builtinAgentToolLoopSources[id];
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// src/loopAPI/agent-tool-loop/compaction.ts
|
|
524
|
-
function countTurns(messages) {
|
|
525
|
-
let turns = 0;
|
|
526
|
-
for (const message of messages) {
|
|
527
|
-
if (message.role === "user") {
|
|
528
|
-
turns++;
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
return turns;
|
|
532
|
-
}
|
|
533
|
-
function estimateTokens(messages) {
|
|
534
|
-
let total = 0;
|
|
535
|
-
for (const message of messages) {
|
|
536
|
-
const content = typeof message.content === "string" ? message.content : JSON.stringify(message.content);
|
|
537
|
-
total += content.length / 3.5;
|
|
538
|
-
}
|
|
539
|
-
return Math.ceil(total);
|
|
540
|
-
}
|
|
541
|
-
function buildTruncationSummary(dropped, totalDropped) {
|
|
542
|
-
const turns = countTurns(dropped);
|
|
543
|
-
const oldest = dropped[0];
|
|
544
|
-
const newest = dropped[dropped.length - 1];
|
|
545
|
-
const oldestTime = oldest?.timestamp ? new Date(oldest.timestamp).toISOString() : "unknown";
|
|
546
|
-
const newestTime = newest?.timestamp ? new Date(newest.timestamp).toISOString() : "unknown";
|
|
547
|
-
return `[context-summary] ${totalDropped} earlier messages (${turns} turns, ${oldestTime} \u2192 ${newestTime}) were compacted. Key topics: see recent messages below.`;
|
|
548
|
-
}
|
|
549
|
-
function createSummaryMessage(conversationId, summaryText, baseMessage) {
|
|
550
|
-
return {
|
|
551
|
-
messageId: `${conversationId}:compacted:${Date.now().toString(36)}`,
|
|
552
|
-
conversationId,
|
|
553
|
-
originNodeId: baseMessage.originNodeId,
|
|
554
|
-
timestamp: Date.now(),
|
|
555
|
-
lamportClock: -1,
|
|
556
|
-
// Will be replaced by AgentToolLoop
|
|
557
|
-
role: "assistant",
|
|
558
|
-
content: summaryText,
|
|
559
|
-
metadata: { compacted: true }
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
function compactMessages(messages, options = {}) {
|
|
563
|
-
const recentTurnsToKeep = options.recentTurnsToKeep ?? 4;
|
|
564
|
-
if (messages.length <= recentTurnsToKeep * 2) {
|
|
565
|
-
return { messages, compacted: false, droppedCount: 0, summaryText: "" };
|
|
566
|
-
}
|
|
567
|
-
const userIndices = [];
|
|
568
|
-
for (let index = 0; index < messages.length; index++) {
|
|
569
|
-
if (messages[index].role === "user") {
|
|
570
|
-
userIndices.push(index);
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
if (userIndices.length <= recentTurnsToKeep) {
|
|
574
|
-
return { messages, compacted: false, droppedCount: 0, summaryText: "" };
|
|
575
|
-
}
|
|
576
|
-
const keepStartIndex = userIndices[userIndices.length - recentTurnsToKeep];
|
|
577
|
-
const dropped = messages.slice(0, keepStartIndex);
|
|
578
|
-
const kept = messages.slice(keepStartIndex);
|
|
579
|
-
if (dropped.length === 0) {
|
|
580
|
-
return { messages, compacted: false, droppedCount: 0, summaryText: "" };
|
|
581
|
-
}
|
|
582
|
-
const conversationId = messages[0]?.conversationId ?? "unknown";
|
|
583
|
-
const summaryText = buildTruncationSummary(dropped, dropped.length);
|
|
584
|
-
const summaryMessage = createSummaryMessage(conversationId, summaryText, messages[0]);
|
|
585
|
-
const compacted = [summaryMessage, ...kept];
|
|
586
|
-
return {
|
|
587
|
-
messages: compacted,
|
|
588
|
-
compacted: true,
|
|
589
|
-
droppedCount: dropped.length,
|
|
590
|
-
summaryText
|
|
591
|
-
};
|
|
592
|
-
}
|
|
593
|
-
function shouldCompact(messages, threshold) {
|
|
594
|
-
const t = threshold ?? 50;
|
|
595
|
-
return messages.length > t;
|
|
596
|
-
}
|
|
597
|
-
async function autoCompact(messages, options = {}) {
|
|
598
|
-
const recentTurnsToKeep = options.recentTurnsToKeep ?? 4;
|
|
599
|
-
if (options.maxTokens && options.maxTokens > 0) {
|
|
600
|
-
const currentTokens = estimateTokens(messages);
|
|
601
|
-
if (currentTokens <= options.maxTokens) {
|
|
602
|
-
return { messages, compacted: false, droppedCount: 0, summaryText: "" };
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
if (options.useLlmSummary !== false && options.llmProvider?.model != null) {
|
|
606
|
-
try {
|
|
607
|
-
const result = await llmCompact(messages, options.llmProvider, recentTurnsToKeep);
|
|
608
|
-
if (result) return result;
|
|
609
|
-
} catch {
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
return compactMessages(messages, { ...options });
|
|
613
|
-
}
|
|
614
|
-
async function llmCompact(messages, llmProvider, recentTurnsToKeep) {
|
|
615
|
-
const userIndices = [];
|
|
616
|
-
for (let index = 0; index < messages.length; index++) {
|
|
617
|
-
if (messages[index].role === "user") {
|
|
618
|
-
userIndices.push(index);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
if (userIndices.length <= recentTurnsToKeep) {
|
|
622
|
-
return null;
|
|
623
|
-
}
|
|
624
|
-
const keepStartIndex = userIndices[userIndices.length - recentTurnsToKeep];
|
|
625
|
-
const toSummarize = messages.slice(0, keepStartIndex);
|
|
626
|
-
const kept = messages.slice(keepStartIndex);
|
|
627
|
-
if (toSummarize.length === 0) return null;
|
|
628
|
-
const conversationId = messages[0]?.conversationId ?? "unknown";
|
|
629
|
-
const conversationText = toSummarize.map((m) => {
|
|
630
|
-
const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
631
|
-
const toolId = typeof m.metadata?.toolId === "string" ? m.metadata.toolId : "unknown";
|
|
632
|
-
const roleLabel = m.role === "tool" ? `[tool: ${toolId}]` : `[${m.role}]`;
|
|
633
|
-
if (m.role === "tool" && content.length > 500) {
|
|
634
|
-
return `${roleLabel} ${content.slice(0, 500)}... (truncated)`;
|
|
635
|
-
}
|
|
636
|
-
return `${roleLabel} ${content}`;
|
|
637
|
-
}).join("\n\n");
|
|
638
|
-
const prompt = `Summarize the following conversation excerpt. Be concise but capture key decisions, action items, tool calls, and important context. Focus on information useful for continuing the conversation.
|
|
639
|
-
|
|
640
|
-
<conversation>
|
|
641
|
-
${conversationText.slice(0, 8e3)}
|
|
642
|
-
</conversation>
|
|
643
|
-
|
|
644
|
-
Provide a brief summary (3-5 paragraphs) of the key points.`;
|
|
645
|
-
try {
|
|
646
|
-
const summaryText = await generateSummary(llmProvider, prompt);
|
|
647
|
-
if (!summaryText || summaryText.length < 10) return null;
|
|
648
|
-
const summaryMessage = createSummaryMessage(
|
|
649
|
-
conversationId,
|
|
650
|
-
`[context-summary] ${summaryText}`,
|
|
651
|
-
messages[0]
|
|
652
|
-
);
|
|
653
|
-
return {
|
|
654
|
-
messages: [summaryMessage, ...kept],
|
|
655
|
-
compacted: true,
|
|
656
|
-
droppedCount: toSummarize.length,
|
|
657
|
-
summaryText
|
|
658
|
-
};
|
|
659
|
-
} catch {
|
|
660
|
-
return null;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
async function generateSummary(llmProvider, prompt) {
|
|
664
|
-
if (typeof llmProvider.chat !== "function") return null;
|
|
665
|
-
const raw = llmProvider.chat({ messages: [{ role: "user", content: prompt }] });
|
|
666
|
-
let resolved = raw;
|
|
667
|
-
if (resolved != null && typeof resolved.then === "function") {
|
|
668
|
-
resolved = await resolved;
|
|
669
|
-
}
|
|
670
|
-
if (resolved != null && typeof resolved[Symbol.asyncIterator] === "function") {
|
|
671
|
-
let text = "";
|
|
672
|
-
for await (const chunk of resolved) {
|
|
673
|
-
text += chunkToText(chunk);
|
|
674
|
-
}
|
|
675
|
-
return text || null;
|
|
676
|
-
}
|
|
677
|
-
if (typeof resolved === "string") return resolved;
|
|
678
|
-
return null;
|
|
679
|
-
}
|
|
680
|
-
function chunkToText(chunk) {
|
|
681
|
-
if (typeof chunk === "string") return chunk;
|
|
682
|
-
if (chunk != null && typeof chunk === "object" && "content" in chunk) {
|
|
683
|
-
const content = chunk.content;
|
|
684
|
-
return typeof content === "string" ? content : JSON.stringify(content);
|
|
685
|
-
}
|
|
686
|
-
return JSON.stringify(chunk);
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// src/loopAPI/agent-tool-loop/scriptLoader.ts
|
|
690
|
-
async function loadAgentToolLoopScript(scriptReference, options = {}) {
|
|
691
|
-
return loadAgentLoopScript(scriptReference, {
|
|
692
|
-
...options,
|
|
693
|
-
getBuiltinScriptSource: getBuiltinAgentToolLoopSource,
|
|
694
|
-
scriptType: "AgentToolLoop script"
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
// src/tools/pluginRegistry.ts
|
|
699
|
-
var defaultPluginRegistry = /* @__PURE__ */ new Map();
|
|
700
|
-
var pluginRegistry = defaultPluginRegistry;
|
|
701
|
-
var activeOverride = null;
|
|
702
|
-
function getActivePluginRegistry() {
|
|
703
|
-
return activeOverride ?? defaultPluginRegistry;
|
|
704
|
-
}
|
|
705
|
-
function runWithPluginRegistry(registry, function_) {
|
|
706
|
-
const previous = activeOverride;
|
|
707
|
-
activeOverride = registry;
|
|
708
|
-
try {
|
|
709
|
-
return function_();
|
|
710
|
-
} finally {
|
|
711
|
-
activeOverride = previous;
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
function createHookSlot() {
|
|
715
|
-
const handlers = [];
|
|
716
|
-
return {
|
|
717
|
-
handlers,
|
|
718
|
-
tapAsync(_name, function_) {
|
|
719
|
-
handlers.push(function_);
|
|
720
|
-
},
|
|
721
|
-
async promise(context) {
|
|
722
|
-
for (const function_ of handlers) {
|
|
723
|
-
await new Promise((resolve) => {
|
|
724
|
-
function_(context, resolve);
|
|
725
|
-
});
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
function createAgentFrameworkHooks() {
|
|
731
|
-
return {
|
|
732
|
-
processPrompts: createHookSlot(),
|
|
733
|
-
finalizePrompts: createHookSlot(),
|
|
734
|
-
postProcess: createHookSlot(),
|
|
735
|
-
userMessageReceived: createHookSlot(),
|
|
736
|
-
agentStatusChanged: createHookSlot(),
|
|
737
|
-
toolExecuted: createHookSlot(),
|
|
738
|
-
responseUpdate: createHookSlot(),
|
|
739
|
-
responseComplete: createHookSlot()
|
|
740
|
-
};
|
|
741
|
-
}
|
|
742
|
-
var hookHandlers = {};
|
|
743
|
-
async function runProcessPromptsHooks(_hooks, context) {
|
|
744
|
-
const slot = _hooks.processPrompts;
|
|
745
|
-
const fns = slot?.handlers ?? hookHandlers.processPrompts ?? [];
|
|
746
|
-
for (const function_ of fns) {
|
|
747
|
-
await new Promise((resolve) => {
|
|
748
|
-
function_(context, resolve);
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
return context;
|
|
752
|
-
}
|
|
753
|
-
async function runResponseCompleteHooks(hooks, context) {
|
|
754
|
-
await hooks.responseComplete.promise(context);
|
|
755
|
-
}
|
|
756
|
-
async function runPostProcessHooks(hooks, context) {
|
|
757
|
-
await hooks.postProcess.promise(context);
|
|
758
|
-
}
|
|
759
|
-
async function runToolExecutedHooks(hooks, context) {
|
|
760
|
-
await hooks.toolExecuted.promise(context);
|
|
761
|
-
}
|
|
762
|
-
function resolvePromptPluginMap(context) {
|
|
763
|
-
const fromTools = context.tools?.getPromptPlugins?.();
|
|
764
|
-
if (fromTools) return fromTools;
|
|
765
|
-
return getActivePluginRegistry();
|
|
766
|
-
}
|
|
767
|
-
async function createHooksWithPlugins(agentFrameworkConfig, options) {
|
|
768
|
-
const reg = options?.pluginRegistry ?? getActivePluginRegistry();
|
|
769
|
-
const hooks = createAgentFrameworkHooks();
|
|
770
|
-
if (agentFrameworkConfig.plugins) {
|
|
771
|
-
for (const pluginConfig of agentFrameworkConfig.plugins) {
|
|
772
|
-
const { toolId } = pluginConfig;
|
|
773
|
-
const plugin = reg.get(toolId);
|
|
774
|
-
if (plugin) {
|
|
775
|
-
plugin(hooks);
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
return {
|
|
780
|
-
hooks,
|
|
781
|
-
pluginConfigs: agentFrameworkConfig.plugins ?? []
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// src/promptUtilities/responsePatternUtility.ts
|
|
786
|
-
import JSON5 from "json5";
|
|
787
|
-
var MAX_FALLBACK_INPUT_LENGTH = 1e3;
|
|
788
|
-
function parseToolParameters(parametersText) {
|
|
789
|
-
if (!parametersText || !parametersText.trim()) {
|
|
790
|
-
return {};
|
|
791
|
-
}
|
|
792
|
-
const trimmedText = parametersText.trim();
|
|
793
|
-
try {
|
|
794
|
-
return JSON.parse(trimmedText);
|
|
795
|
-
} catch {
|
|
796
|
-
}
|
|
797
|
-
try {
|
|
798
|
-
return JSON5.parse(trimmedText);
|
|
799
|
-
} catch {
|
|
800
|
-
}
|
|
801
|
-
return { input: trimmedText.substring(0, MAX_FALLBACK_INPUT_LENGTH) };
|
|
802
|
-
}
|
|
803
|
-
function extractFunctionCallsParameters(text) {
|
|
804
|
-
const parameters = {};
|
|
805
|
-
const parameterRegex = /<parameter\s+name="([^"]+)"[^>]*>([^<]*)<\/parameter>/g;
|
|
806
|
-
let m;
|
|
807
|
-
while ((m = parameterRegex.exec(text)) !== null) {
|
|
808
|
-
parameters[m[1]] = m[2].trim();
|
|
809
|
-
}
|
|
810
|
-
return parameters;
|
|
811
|
-
}
|
|
812
|
-
var toolPatterns = [
|
|
813
|
-
{
|
|
814
|
-
name: "tool_use",
|
|
815
|
-
pattern: /<tool_use\s+name="([^"]+)"[^>]*>(.*?)<\/tool_use>/gis,
|
|
816
|
-
extractToolId: (match) => match[1],
|
|
817
|
-
extractParams: (match) => match[2],
|
|
818
|
-
extractOriginalText: (match) => match[0]
|
|
819
|
-
},
|
|
820
|
-
{
|
|
821
|
-
name: "function_call",
|
|
822
|
-
pattern: /<function_call\s+name="([^"]+)"[^>]*>(.*?)<\/function_call>/gis,
|
|
823
|
-
extractToolId: (match) => match[1],
|
|
824
|
-
extractParams: (match) => match[2],
|
|
825
|
-
extractOriginalText: (match) => match[0]
|
|
826
|
-
},
|
|
827
|
-
{
|
|
828
|
-
name: "function_calls_invoke",
|
|
829
|
-
pattern: /<invoke\s+name="([^"]+)"[^>]*>(.*?)<\/invoke>/gis,
|
|
830
|
-
extractToolId: (match) => match[1],
|
|
831
|
-
extractParams: (match) => JSON.stringify(extractFunctionCallsParameters(match[2])),
|
|
832
|
-
extractOriginalText: (match) => match[0]
|
|
833
|
-
}
|
|
834
|
-
];
|
|
835
|
-
function matchToolCalling(responseText) {
|
|
836
|
-
try {
|
|
837
|
-
for (const toolPattern of toolPatterns) {
|
|
838
|
-
toolPattern.pattern.lastIndex = 0;
|
|
839
|
-
const match = toolPattern.pattern.exec(responseText);
|
|
840
|
-
if (match) {
|
|
841
|
-
const toolId = toolPattern.extractToolId(match);
|
|
842
|
-
const parametersText = toolPattern.extractParams(match);
|
|
843
|
-
const originalText = toolPattern.extractOriginalText(match);
|
|
844
|
-
return {
|
|
845
|
-
found: true,
|
|
846
|
-
toolId,
|
|
847
|
-
parameters: parseToolParameters(parametersText),
|
|
848
|
-
originalText
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
return { found: false };
|
|
853
|
-
} catch {
|
|
854
|
-
return { found: false };
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
function matchAllToolCallings(responseText) {
|
|
858
|
-
const calls = [];
|
|
859
|
-
const parallel = /<parallel_tool_calls>/i.test(responseText);
|
|
860
|
-
try {
|
|
861
|
-
for (const toolPattern of toolPatterns) {
|
|
862
|
-
toolPattern.pattern.lastIndex = 0;
|
|
863
|
-
let match;
|
|
864
|
-
while ((match = toolPattern.pattern.exec(responseText)) !== null) {
|
|
865
|
-
calls.push({
|
|
866
|
-
found: true,
|
|
867
|
-
toolId: toolPattern.extractToolId(match),
|
|
868
|
-
parameters: parseToolParameters(toolPattern.extractParams(match)),
|
|
869
|
-
originalText: toolPattern.extractOriginalText(match)
|
|
870
|
-
});
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
} catch {
|
|
874
|
-
}
|
|
875
|
-
return { calls, parallel };
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
// src/storage/nextLamport.ts
|
|
879
|
-
async function nextLamportClockForConversation(storage, conversationId) {
|
|
880
|
-
if (typeof storage.getMaxLamportClockForConversation === "function") {
|
|
881
|
-
const max2 = await storage.getMaxLamportClockForConversation(conversationId);
|
|
882
|
-
return max2 + 1;
|
|
883
|
-
}
|
|
884
|
-
const msgs = await storage.getMessages(conversationId, { mode: "full-content" });
|
|
885
|
-
let max = 0;
|
|
886
|
-
for (const m of msgs) {
|
|
887
|
-
if (typeof m.lamportClock === "number" && m.lamportClock > max) {
|
|
888
|
-
max = m.lamportClock;
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
return max + 1;
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
// src/loopAPI/hooks/registry.ts
|
|
895
|
-
var HookRegistry = class {
|
|
896
|
-
hookRegistry = /* @__PURE__ */ new Map();
|
|
897
|
-
hookOrder = /* @__PURE__ */ new Map();
|
|
898
|
-
/**
|
|
899
|
-
* Register a hook handler for a specific lifecycle event.
|
|
900
|
-
*/
|
|
901
|
-
registerHook(type, handler, name) {
|
|
902
|
-
const key = name ?? `hook:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 8)}`;
|
|
903
|
-
const handlers = this.hookOrder.get(type) ?? [];
|
|
904
|
-
handlers.push(handler);
|
|
905
|
-
this.hookOrder.set(type, handlers);
|
|
906
|
-
const map = this.hookRegistry.get(type) ?? /* @__PURE__ */ new Map();
|
|
907
|
-
map.set(key, handler);
|
|
908
|
-
this.hookRegistry.set(type, map);
|
|
909
|
-
}
|
|
910
|
-
/**
|
|
911
|
-
* Unregister a specific hook handler by name.
|
|
912
|
-
*/
|
|
913
|
-
unregisterHook(type, name) {
|
|
914
|
-
const map = this.hookRegistry.get(type);
|
|
915
|
-
if (!map) return false;
|
|
916
|
-
const deleted = map.delete(name);
|
|
917
|
-
if (deleted) {
|
|
918
|
-
this.hookOrder.set(type, Array.from(map.values()));
|
|
919
|
-
}
|
|
920
|
-
return deleted;
|
|
921
|
-
}
|
|
922
|
-
/**
|
|
923
|
-
* Execute all registered hooks for a given type in registration order.
|
|
924
|
-
*/
|
|
925
|
-
async executeHooks(type, context, data) {
|
|
926
|
-
const handlers = this.hookOrder.get(type);
|
|
927
|
-
if (!handlers || handlers.length === 0) {
|
|
928
|
-
return { allowed: true };
|
|
929
|
-
}
|
|
930
|
-
let currentData = data;
|
|
931
|
-
let mergedModified;
|
|
932
|
-
let permissionAction;
|
|
933
|
-
for (const handler of handlers) {
|
|
934
|
-
try {
|
|
935
|
-
const result = await handler(context, currentData);
|
|
936
|
-
if (result.modified) {
|
|
937
|
-
mergedModified = { ...mergedModified ?? {}, ...result.modified };
|
|
938
|
-
currentData = { ...currentData, ...result.modified };
|
|
939
|
-
}
|
|
940
|
-
if (result.permissionAction && result.permissionAction !== "allow") {
|
|
941
|
-
permissionAction = result.permissionAction;
|
|
942
|
-
}
|
|
943
|
-
if (!result.allowed) {
|
|
944
|
-
return {
|
|
945
|
-
...result,
|
|
946
|
-
modified: mergedModified ?? result.modified,
|
|
947
|
-
permissionAction: permissionAction ?? result.permissionAction
|
|
948
|
-
};
|
|
949
|
-
}
|
|
950
|
-
} catch (error) {
|
|
951
|
-
return {
|
|
952
|
-
allowed: false,
|
|
953
|
-
reason: error instanceof Error ? error.message : "Hook execution failed",
|
|
954
|
-
modified: mergedModified,
|
|
955
|
-
permissionAction
|
|
956
|
-
};
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
const finalResult = { allowed: true };
|
|
960
|
-
if (mergedModified) finalResult.modified = mergedModified;
|
|
961
|
-
if (permissionAction) finalResult.permissionAction = permissionAction;
|
|
962
|
-
return finalResult;
|
|
963
|
-
}
|
|
964
|
-
/**
|
|
965
|
-
* Check if any hooks are registered for a given type.
|
|
966
|
-
*/
|
|
967
|
-
hasHooks(type) {
|
|
968
|
-
const map = this.hookRegistry.get(type);
|
|
969
|
-
return map != null && map.size > 0;
|
|
970
|
-
}
|
|
971
|
-
/**
|
|
972
|
-
* Remove all registered hooks of all types.
|
|
973
|
-
*/
|
|
974
|
-
clearHooks() {
|
|
975
|
-
this.hookRegistry.clear();
|
|
976
|
-
this.hookOrder.clear();
|
|
977
|
-
}
|
|
978
|
-
/**
|
|
979
|
-
* List all hook types that have at least one registered handler.
|
|
980
|
-
*/
|
|
981
|
-
listRegisteredHookTypes() {
|
|
982
|
-
const types = [];
|
|
983
|
-
for (const [type, map] of this.hookRegistry) {
|
|
984
|
-
if (map.size > 0) {
|
|
985
|
-
types.push(type);
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
return types;
|
|
989
|
-
}
|
|
990
|
-
/**
|
|
991
|
-
* Get the count of registered handlers for a hook type.
|
|
992
|
-
*/
|
|
993
|
-
getHookCount(type) {
|
|
994
|
-
const map = this.hookRegistry.get(type);
|
|
995
|
-
return map?.size ?? 0;
|
|
996
|
-
}
|
|
997
|
-
};
|
|
998
|
-
var defaultHookRegistry = new HookRegistry();
|
|
999
|
-
function getDefaultHookRegistry() {
|
|
1000
|
-
return defaultHookRegistry;
|
|
1001
|
-
}
|
|
1002
|
-
function registerHook(type, handler, name) {
|
|
1003
|
-
defaultHookRegistry.registerHook(type, handler, name);
|
|
1004
|
-
}
|
|
1005
|
-
function unregisterHook(type, name) {
|
|
1006
|
-
return defaultHookRegistry.unregisterHook(type, name);
|
|
1007
|
-
}
|
|
1008
|
-
async function executeHooks(type, context, data) {
|
|
1009
|
-
return defaultHookRegistry.executeHooks(type, context, data);
|
|
1010
|
-
}
|
|
1011
|
-
function hasHooks(type) {
|
|
1012
|
-
return defaultHookRegistry.hasHooks(type);
|
|
1013
|
-
}
|
|
1014
|
-
function clearHooks() {
|
|
1015
|
-
defaultHookRegistry.clearHooks();
|
|
1016
|
-
}
|
|
1017
|
-
function listRegisteredHookTypes() {
|
|
1018
|
-
return defaultHookRegistry.listRegisteredHookTypes();
|
|
1019
|
-
}
|
|
1020
|
-
function getHookCount(type) {
|
|
1021
|
-
return defaultHookRegistry.getHookCount(type);
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
// src/promptUtilities/promptConcat.ts
|
|
1025
|
-
function collectPromptSourcePaths(prompts, basePath = "agentFrameworkConfig.prompts") {
|
|
1026
|
-
const out = {};
|
|
1027
|
-
function walk(nodes, prefix) {
|
|
1028
|
-
nodes.forEach((n, index) => {
|
|
1029
|
-
const p = `${prefix}.${index}`;
|
|
1030
|
-
if (n.id) {
|
|
1031
|
-
out[n.id] = p;
|
|
1032
|
-
}
|
|
1033
|
-
if (n.children?.length) {
|
|
1034
|
-
walk(n.children, `${p}.children`);
|
|
1035
|
-
}
|
|
1036
|
-
});
|
|
1037
|
-
}
|
|
1038
|
-
walk(prompts, basePath);
|
|
1039
|
-
return out;
|
|
1040
|
-
}
|
|
1041
|
-
var logger = {
|
|
1042
|
-
debug: (..._arguments) => {
|
|
1043
|
-
},
|
|
1044
|
-
info: (..._arguments) => {
|
|
1045
|
-
},
|
|
1046
|
-
warn: (..._arguments) => {
|
|
1047
|
-
},
|
|
1048
|
-
error: (..._arguments) => {
|
|
1049
|
-
}
|
|
1050
|
-
};
|
|
1051
|
-
function findPromptById(prompts, id) {
|
|
1052
|
-
for (let index = 0; index < prompts.length; index++) {
|
|
1053
|
-
const prompt = prompts[index];
|
|
1054
|
-
if (prompt.id === id) {
|
|
1055
|
-
return { prompt, parent: prompts, index };
|
|
1056
|
-
}
|
|
1057
|
-
if (prompt.children) {
|
|
1058
|
-
const found = findPromptById(prompt.children, id);
|
|
1059
|
-
if (found) return found;
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
return void 0;
|
|
1063
|
-
}
|
|
1064
|
-
function flattenPrompts(prompts) {
|
|
1065
|
-
const result = [];
|
|
1066
|
-
function processPrompt(prompt) {
|
|
1067
|
-
let text = prompt.text ?? "";
|
|
1068
|
-
if (prompt.children) {
|
|
1069
|
-
for (const child of prompt.children) {
|
|
1070
|
-
if (!child.role) {
|
|
1071
|
-
text += processPrompt(child);
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
return text;
|
|
1076
|
-
}
|
|
1077
|
-
function collectRolePrompts(nodes) {
|
|
1078
|
-
for (const prompt of nodes) {
|
|
1079
|
-
if (prompt.enabled === false) continue;
|
|
1080
|
-
const content = processPrompt(prompt);
|
|
1081
|
-
if (content.trim() || prompt.role) {
|
|
1082
|
-
result.push({
|
|
1083
|
-
role: prompt.role ?? "system",
|
|
1084
|
-
content: content.trim()
|
|
1085
|
-
});
|
|
1086
|
-
}
|
|
1087
|
-
if (prompt.children) {
|
|
1088
|
-
collectRolePrompts(prompt.children);
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
collectRolePrompts(prompts);
|
|
1093
|
-
return result;
|
|
1094
|
-
}
|
|
1095
|
-
async function* promptConcatStream(agentConfig, messages, agentFrameworkContext, options) {
|
|
1096
|
-
const frameworkConfig = agentConfig.agentFrameworkConfig;
|
|
1097
|
-
const promptConfigs = frameworkConfig?.prompts ?? [];
|
|
1098
|
-
const plugins = frameworkConfig?.plugins ?? [];
|
|
1099
|
-
const hooks = createAgentFrameworkHooks();
|
|
1100
|
-
const pluginMap = resolvePromptPluginMap(agentFrameworkContext);
|
|
1101
|
-
for (const plugin of plugins) {
|
|
1102
|
-
const entry = pluginMap.get(plugin.toolId);
|
|
1103
|
-
if (entry) entry(hooks);
|
|
1104
|
-
}
|
|
1105
|
-
let processedContext = { prompts: promptConfigs };
|
|
1106
|
-
for (let index = 0; index < plugins.length; index++) {
|
|
1107
|
-
const plugin = plugins[index];
|
|
1108
|
-
processedContext = await runProcessPromptsHooks(hooks, {
|
|
1109
|
-
prompts: processedContext.prompts,
|
|
1110
|
-
messages,
|
|
1111
|
-
toolConfig: plugin,
|
|
1112
|
-
pluginIndex: index,
|
|
1113
|
-
agentFrameworkContext
|
|
1114
|
-
});
|
|
1115
|
-
}
|
|
1116
|
-
const processed = processedContext.prompts;
|
|
1117
|
-
const flat = flattenPrompts(processed);
|
|
1118
|
-
const last = messages[messages.length - 1];
|
|
1119
|
-
if (last && last.role === "user") {
|
|
1120
|
-
const content = last.content;
|
|
1121
|
-
const fileMeta = last.metadata?.file;
|
|
1122
|
-
if (fileMeta?.path && options?.readAttachmentFile) {
|
|
1123
|
-
try {
|
|
1124
|
-
const buf = await options.readAttachmentFile(fileMeta.path);
|
|
1125
|
-
flat.push({
|
|
1126
|
-
role: "user",
|
|
1127
|
-
content: [
|
|
1128
|
-
{ type: "image", image: buf },
|
|
1129
|
-
{ type: "text", text: content }
|
|
1130
|
-
]
|
|
1131
|
-
});
|
|
1132
|
-
} catch (error) {
|
|
1133
|
-
logger.error("failed to read attached file", { error, path: fileMeta.path });
|
|
1134
|
-
}
|
|
1135
|
-
} else if (fileMeta?.path && !options?.readAttachmentFile) {
|
|
1136
|
-
flat.push({
|
|
1137
|
-
role: "user",
|
|
1138
|
-
content: `[attached path: ${fileMeta.path}]
|
|
1139
|
-
${content}`
|
|
1140
|
-
});
|
|
1141
|
-
} else {
|
|
1142
|
-
flat.push({ role: "user", content });
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
const state = {
|
|
1146
|
-
processedPrompts: processed,
|
|
1147
|
-
flatPrompts: flat,
|
|
1148
|
-
step: "complete",
|
|
1149
|
-
isComplete: true,
|
|
1150
|
-
sourcePaths: collectPromptSourcePaths(processed)
|
|
1151
|
-
};
|
|
1152
|
-
yield state;
|
|
1153
|
-
return state;
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
// src/tools/structuredToolResult.ts
|
|
1157
|
-
var MEMELOOP_STRUCTURED_TOOL_KEY = "__memeloopToolResult";
|
|
1158
|
-
function truncateToolSummary(s, max = 2e3) {
|
|
1159
|
-
if (s.length <= max) return s;
|
|
1160
|
-
return `${s.slice(0, max - 3)}...`;
|
|
1161
|
-
}
|
|
1162
|
-
function extractMemeloopStructuredToolPayload(raw) {
|
|
1163
|
-
if (raw === null || typeof raw !== "object") return null;
|
|
1164
|
-
const o = raw;
|
|
1165
|
-
const payload = o[MEMELOOP_STRUCTURED_TOOL_KEY];
|
|
1166
|
-
if (payload === null || typeof payload !== "object") return null;
|
|
1167
|
-
const p = payload;
|
|
1168
|
-
if (typeof p.summary !== "string" || p.summary.length === 0) return null;
|
|
1169
|
-
const awaitSessionId = typeof p.awaitSessionId === "string" && p.awaitSessionId.length > 0 ? p.awaitSessionId : void 0;
|
|
1170
|
-
return {
|
|
1171
|
-
summary: p.summary,
|
|
1172
|
-
detailRef: p.detailRef,
|
|
1173
|
-
awaitSessionId
|
|
1174
|
-
};
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
// src/permission/engine.ts
|
|
1178
|
-
function matchPattern(toolName, pattern) {
|
|
1179
|
-
if (pattern === toolName) return true;
|
|
1180
|
-
if (pattern === "*") return true;
|
|
1181
|
-
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*");
|
|
1182
|
-
return new RegExp(`^${escaped}$`).test(toolName);
|
|
1183
|
-
}
|
|
1184
|
-
function mergePermissionSets(sets) {
|
|
1185
|
-
const merged = /* @__PURE__ */ new Map();
|
|
1186
|
-
merged.set("allow", /* @__PURE__ */ new Map());
|
|
1187
|
-
merged.set("deny", /* @__PURE__ */ new Map());
|
|
1188
|
-
merged.set("ask", /* @__PURE__ */ new Map());
|
|
1189
|
-
for (const set of sets) {
|
|
1190
|
-
for (const rule of set.rules) {
|
|
1191
|
-
for (const [action, map] of merged) {
|
|
1192
|
-
if (action !== rule.action) {
|
|
1193
|
-
map.delete(rule.toolPattern);
|
|
1194
|
-
}
|
|
1195
|
-
}
|
|
1196
|
-
merged.get(rule.action).set(rule.toolPattern, rule.action);
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
return {
|
|
1200
|
-
allow: [...merged.get("allow").keys()],
|
|
1201
|
-
deny: [...merged.get("deny").keys()],
|
|
1202
|
-
ask: [...merged.get("ask").keys()]
|
|
1203
|
-
};
|
|
1204
|
-
}
|
|
1205
|
-
function checkPermission(toolName, merged) {
|
|
1206
|
-
const isWildcard = (p) => p.includes("*");
|
|
1207
|
-
for (const pattern of merged.deny) {
|
|
1208
|
-
if (!isWildcard(pattern) && matchPattern(toolName, pattern)) return "deny";
|
|
1209
|
-
}
|
|
1210
|
-
for (const pattern of merged.ask) {
|
|
1211
|
-
if (!isWildcard(pattern) && matchPattern(toolName, pattern)) return "ask";
|
|
1212
|
-
}
|
|
1213
|
-
for (const pattern of merged.allow) {
|
|
1214
|
-
if (!isWildcard(pattern) && matchPattern(toolName, pattern)) return "allow";
|
|
1215
|
-
}
|
|
1216
|
-
for (const pattern of merged.deny) {
|
|
1217
|
-
if (isWildcard(pattern) && matchPattern(toolName, pattern)) return "deny";
|
|
1218
|
-
}
|
|
1219
|
-
for (const pattern of merged.ask) {
|
|
1220
|
-
if (isWildcard(pattern) && matchPattern(toolName, pattern)) return "ask";
|
|
1221
|
-
}
|
|
1222
|
-
for (const pattern of merged.allow) {
|
|
1223
|
-
if (isWildcard(pattern) && matchPattern(toolName, pattern)) return "allow";
|
|
1224
|
-
}
|
|
1225
|
-
const hasAnyRules = merged.allow.length > 0 || merged.deny.length > 0 || merged.ask.length > 0;
|
|
1226
|
-
return hasAnyRules ? "deny" : "allow";
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
// src/permission/storage.ts
|
|
1230
|
-
var TABLE_NAME = "permissions";
|
|
1231
|
-
var STORAGE_KEY = "user";
|
|
1232
|
-
function loadUserPermissions(database) {
|
|
1233
|
-
try {
|
|
1234
|
-
const row = database.prepare(`SELECT rulesJson FROM ${TABLE_NAME} WHERE source = ? LIMIT 1`).get(STORAGE_KEY);
|
|
1235
|
-
if (row?.rulesJson) {
|
|
1236
|
-
return JSON.parse(row.rulesJson);
|
|
1237
|
-
}
|
|
1238
|
-
} catch {
|
|
1239
|
-
}
|
|
1240
|
-
return { rules: [], source: "user" };
|
|
1241
|
-
}
|
|
1242
|
-
function saveUserPermissions(database, set) {
|
|
1243
|
-
database.prepare(
|
|
1244
|
-
`INSERT OR REPLACE INTO ${TABLE_NAME} (source, rulesJson, updatedAt)
|
|
1245
|
-
VALUES (?, ?, ?)`
|
|
1246
|
-
).run(set.source, JSON.stringify(set), Date.now());
|
|
1247
|
-
}
|
|
1248
|
-
var PERMISSIONS_TABLE_DDL = `
|
|
1249
|
-
CREATE TABLE IF NOT EXISTS ${TABLE_NAME} (
|
|
1250
|
-
source TEXT PRIMARY KEY,
|
|
1251
|
-
rulesJson TEXT NOT NULL,
|
|
1252
|
-
updatedAt INTEGER NOT NULL
|
|
1253
|
-
);
|
|
1254
|
-
`;
|
|
1255
|
-
|
|
1256
|
-
// src/tools/approval.ts
|
|
1257
|
-
var pendingApprovals = /* @__PURE__ */ new Map();
|
|
1258
|
-
var approvalListeners = /* @__PURE__ */ new Set();
|
|
1259
|
-
function onApprovalRequest(listener) {
|
|
1260
|
-
approvalListeners.add(listener);
|
|
1261
|
-
return () => {
|
|
1262
|
-
approvalListeners.delete(listener);
|
|
1263
|
-
};
|
|
1264
|
-
}
|
|
1265
|
-
function evaluateApproval(approval, toolName, parameters) {
|
|
1266
|
-
if (!approval || approval.mode === "auto") {
|
|
1267
|
-
return "allow";
|
|
1268
|
-
}
|
|
1269
|
-
const callContent = JSON.stringify({ tool: toolName, parameters });
|
|
1270
|
-
if (approval.denyPatterns?.length) {
|
|
1271
|
-
for (const pattern of approval.denyPatterns) {
|
|
1272
|
-
try {
|
|
1273
|
-
if (new RegExp(pattern, "i").test(callContent)) {
|
|
1274
|
-
return "deny";
|
|
1275
|
-
}
|
|
1276
|
-
} catch {
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
if (approval.allowPatterns?.length) {
|
|
1281
|
-
for (const pattern of approval.allowPatterns) {
|
|
1282
|
-
try {
|
|
1283
|
-
if (new RegExp(pattern, "i").test(callContent)) {
|
|
1284
|
-
return "allow";
|
|
1285
|
-
}
|
|
1286
|
-
} catch {
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
return "pending";
|
|
1291
|
-
}
|
|
1292
|
-
function requestApproval(request, timeoutMs = 6e4) {
|
|
1293
|
-
return new Promise((resolve) => {
|
|
1294
|
-
pendingApprovals.set(request.approvalId, { request, resolve });
|
|
1295
|
-
for (const listener of approvalListeners) {
|
|
1296
|
-
try {
|
|
1297
|
-
listener(request);
|
|
1298
|
-
} catch {
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1301
|
-
if (timeoutMs > 0) {
|
|
1302
|
-
setTimeout(() => {
|
|
1303
|
-
if (pendingApprovals.has(request.approvalId)) {
|
|
1304
|
-
pendingApprovals.delete(request.approvalId);
|
|
1305
|
-
resolve("deny");
|
|
1306
|
-
}
|
|
1307
|
-
}, timeoutMs);
|
|
1308
|
-
}
|
|
1309
|
-
});
|
|
1310
|
-
}
|
|
1311
|
-
function resolveApproval(approvalId, decision) {
|
|
1312
|
-
const pending2 = pendingApprovals.get(approvalId);
|
|
1313
|
-
if (pending2) {
|
|
1314
|
-
pendingApprovals.delete(approvalId);
|
|
1315
|
-
pending2.resolve(decision);
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
function getPendingApprovals() {
|
|
1319
|
-
return [...pendingApprovals.values()].map((p) => p.request);
|
|
1320
|
-
}
|
|
1321
|
-
function cancelPendingApprovals(agentId) {
|
|
1322
|
-
for (const [id, pending2] of pendingApprovals) {
|
|
1323
|
-
if (pending2.request.agentId === agentId) {
|
|
1324
|
-
pendingApprovals.delete(id);
|
|
1325
|
-
pending2.resolve("deny");
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
|
-
// src/promptUtilities/responseConcat.ts
|
|
1331
|
-
function cloneResponses(responses) {
|
|
1332
|
-
return structuredClone(responses);
|
|
1333
|
-
}
|
|
1334
|
-
async function responseConcat(agentFrameworkConfig, llmResponse, agentFrameworkContext, messages) {
|
|
1335
|
-
const responses = Array.isArray(agentFrameworkConfig?.response) ? cloneResponses(agentFrameworkConfig.response) : [];
|
|
1336
|
-
const toolConfigs = (Array.isArray(agentFrameworkConfig.plugins) ? agentFrameworkConfig.plugins : []).filter((t) => t.enabled !== false);
|
|
1337
|
-
const hooks = createAgentFrameworkHooks();
|
|
1338
|
-
const pluginMap = resolvePromptPluginMap(agentFrameworkContext);
|
|
1339
|
-
for (const tool of toolConfigs) {
|
|
1340
|
-
const builtInTool = pluginMap.get(tool.toolId);
|
|
1341
|
-
if (builtInTool) {
|
|
1342
|
-
builtInTool(hooks);
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
let yieldNextRoundTo;
|
|
1346
|
-
let toolCallInfo;
|
|
1347
|
-
for (const tool of toolConfigs) {
|
|
1348
|
-
const responseContext = {
|
|
1349
|
-
agentFrameworkContext,
|
|
1350
|
-
messages,
|
|
1351
|
-
prompts: [],
|
|
1352
|
-
toolConfig: tool,
|
|
1353
|
-
llmResponse,
|
|
1354
|
-
responses,
|
|
1355
|
-
metadata: {},
|
|
1356
|
-
actions: {}
|
|
1357
|
-
};
|
|
1358
|
-
await runPostProcessHooks(hooks, responseContext);
|
|
1359
|
-
if (responseContext.actions?.yieldNextRoundTo) {
|
|
1360
|
-
yieldNextRoundTo = responseContext.actions.yieldNextRoundTo;
|
|
1361
|
-
if (responseContext.actions.toolCalling) {
|
|
1362
|
-
toolCallInfo = responseContext.actions.toolCalling;
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
const processedResponse = flattenResponses(responses);
|
|
1367
|
-
return {
|
|
1368
|
-
processedResponse: processedResponse || llmResponse,
|
|
1369
|
-
yieldNextRoundTo,
|
|
1370
|
-
toolCallInfo
|
|
1371
|
-
};
|
|
1372
|
-
}
|
|
1373
|
-
function flattenResponses(responses) {
|
|
1374
|
-
if (responses.length === 0) {
|
|
1375
|
-
return "";
|
|
1376
|
-
}
|
|
1377
|
-
return responses.filter((response) => response.enabled !== false).map((response) => response.text || "").join("\n\n").trim();
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
// src/loopAPI/agent-tool-loop/historyCompaction.ts
|
|
1381
|
-
function compactHistory(history, options) {
|
|
1382
|
-
const maxMessages = options?.contextCompaction?.maxMessages ?? 0;
|
|
1383
|
-
if (maxMessages <= 0 || history.length <= maxMessages) return history;
|
|
1384
|
-
const dropped = history.length - maxMessages;
|
|
1385
|
-
const tail = history.slice(-maxMessages);
|
|
1386
|
-
const summaryMessage = {
|
|
1387
|
-
...tail[0],
|
|
1388
|
-
messageId: `${tail[0]?.conversationId ?? "unknown"}:summary:${Date.now().toString(36)}`,
|
|
1389
|
-
role: "assistant",
|
|
1390
|
-
content: `[context-summary] ${dropped} earlier messages were compacted.`
|
|
1391
|
-
};
|
|
1392
|
-
if (options?.contextCompaction?.replayLastUserMessage === false) return tail;
|
|
1393
|
-
const lastUser = [...history].reverse().find((message) => message.role === "user");
|
|
1394
|
-
if (!lastUser) return [summaryMessage, ...tail];
|
|
1395
|
-
if (tail.some((message) => message.messageId === lastUser.messageId)) return tail;
|
|
1396
|
-
return [summaryMessage, lastUser, ...tail];
|
|
1397
|
-
}
|
|
1398
|
-
function asChatMessages(value) {
|
|
1399
|
-
if (!Array.isArray(value)) return void 0;
|
|
1400
|
-
if (!value.every((message) => message != null && typeof message === "object")) return void 0;
|
|
1401
|
-
return value;
|
|
1402
|
-
}
|
|
1403
|
-
function buildCompactedStep(conversationId, iteration, droppedCount, summaryText) {
|
|
1404
|
-
return {
|
|
1405
|
-
type: "thinking",
|
|
1406
|
-
data: {
|
|
1407
|
-
status: "compacted",
|
|
1408
|
-
conversationId,
|
|
1409
|
-
droppedCount: typeof droppedCount === "number" ? droppedCount : 0,
|
|
1410
|
-
summaryText: typeof summaryText === "string" ? summaryText : "",
|
|
1411
|
-
iteration
|
|
1412
|
-
}
|
|
1413
|
-
};
|
|
1414
|
-
}
|
|
1415
|
-
async function persistSummaryMessage(context, conversationId, summaryMessage) {
|
|
1416
|
-
if (!summaryMessage) return;
|
|
1417
|
-
const lamportSummary = await nextLamportClockForConversation(context.storage, conversationId);
|
|
1418
|
-
await context.storage.appendMessage({
|
|
1419
|
-
...summaryMessage,
|
|
1420
|
-
lamportClock: lamportSummary
|
|
1421
|
-
});
|
|
1422
|
-
}
|
|
1423
|
-
async function maybeApplyContextCompactionHook(options) {
|
|
1424
|
-
const { context, conversationId, iteration, history, agentToolLoopOptions } = options;
|
|
1425
|
-
if (!hasHooks("ContextCompaction")) {
|
|
1426
|
-
return { handled: false, history, steps: [] };
|
|
1427
|
-
}
|
|
1428
|
-
const hookResult = await executeHooks("ContextCompaction", context, {
|
|
1429
|
-
conversationId,
|
|
1430
|
-
iteration,
|
|
1431
|
-
history,
|
|
1432
|
-
autoCompact: agentToolLoopOptions?.autoCompact,
|
|
1433
|
-
contextCompaction: agentToolLoopOptions?.contextCompaction
|
|
1434
|
-
});
|
|
1435
|
-
const modified = hookResult.modified;
|
|
1436
|
-
const modifiedHistory = asChatMessages(modified?.history);
|
|
1437
|
-
const nextHistory = modifiedHistory ?? history;
|
|
1438
|
-
const skipDefault = !hookResult.allowed || modifiedHistory != null || modified?.skipDefault === true;
|
|
1439
|
-
if (!skipDefault) {
|
|
1440
|
-
return { handled: false, history: nextHistory, steps: [] };
|
|
1441
|
-
}
|
|
1442
|
-
const steps = modified?.compacted === true ? [buildCompactedStep(conversationId, iteration, modified.droppedCount, modified.summaryText)] : [];
|
|
1443
|
-
if (modified?.persistSummaryMessage === true) {
|
|
1444
|
-
await persistSummaryMessage(context, conversationId, nextHistory[0]);
|
|
1445
|
-
}
|
|
1446
|
-
return { handled: true, history: nextHistory, steps };
|
|
1447
|
-
}
|
|
1448
|
-
async function applyBuiltInAutoCompact(options) {
|
|
1449
|
-
const { context, conversationId, iteration, agentToolLoopOptions } = options;
|
|
1450
|
-
let history = options.history;
|
|
1451
|
-
const autoCompactOptions = agentToolLoopOptions?.autoCompact;
|
|
1452
|
-
if (!autoCompactOptions) {
|
|
1453
|
-
return { history, steps: [] };
|
|
1454
|
-
}
|
|
1455
|
-
const threshold = autoCompactOptions.threshold ?? 50;
|
|
1456
|
-
if (!shouldCompact(history, threshold)) {
|
|
1457
|
-
return { history, steps: [] };
|
|
1458
|
-
}
|
|
1459
|
-
try {
|
|
1460
|
-
const result = await autoCompact(history, {
|
|
1461
|
-
recentTurnsToKeep: autoCompactOptions.recentTurnsToKeep ?? 4,
|
|
1462
|
-
maxTokens: autoCompactOptions.maxTokens ?? 0,
|
|
1463
|
-
llmProvider: context.llmProvider
|
|
1464
|
-
});
|
|
1465
|
-
if (!result.compacted) {
|
|
1466
|
-
return { history, steps: [] };
|
|
1467
|
-
}
|
|
1468
|
-
history = result.messages;
|
|
1469
|
-
await persistSummaryMessage(context, conversationId, result.messages[0]);
|
|
1470
|
-
return {
|
|
1471
|
-
history,
|
|
1472
|
-
steps: [
|
|
1473
|
-
buildCompactedStep(conversationId, iteration, result.droppedCount, result.summaryText)
|
|
1474
|
-
]
|
|
1475
|
-
};
|
|
1476
|
-
} catch (error) {
|
|
1477
|
-
if (context.logger?.warn) {
|
|
1478
|
-
context.logger.warn("[agentToolLoop] auto-compact failed:", error);
|
|
1479
|
-
} else {
|
|
1480
|
-
console.warn("[agentToolLoop] auto-compact failed:", error);
|
|
1481
|
-
}
|
|
1482
|
-
return { history, steps: [] };
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
async function prepareIterationHistory(options) {
|
|
1486
|
-
const { agentToolLoopOptions } = options;
|
|
1487
|
-
const hookResult = await maybeApplyContextCompactionHook({
|
|
1488
|
-
...options,
|
|
1489
|
-
history: options.rawHistory
|
|
1490
|
-
});
|
|
1491
|
-
if (hookResult.handled) {
|
|
1492
|
-
return hookResult;
|
|
1493
|
-
}
|
|
1494
|
-
const autoCompactResult = await applyBuiltInAutoCompact({
|
|
1495
|
-
...options,
|
|
1496
|
-
history: hookResult.history
|
|
1497
|
-
});
|
|
1498
|
-
return {
|
|
1499
|
-
history: compactHistory(autoCompactResult.history, agentToolLoopOptions),
|
|
1500
|
-
steps: autoCompactResult.steps
|
|
1501
|
-
};
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
// src/loopAPI/agent-tool-loop/llmStream.ts
|
|
1505
|
-
function isAsyncIterable2(value) {
|
|
1506
|
-
return value != null && typeof value[Symbol.asyncIterator] === "function";
|
|
1507
|
-
}
|
|
1508
|
-
function chunkToText2(chunk) {
|
|
1509
|
-
if (typeof chunk === "string") return chunk;
|
|
1510
|
-
if (chunk != null && typeof chunk === "object" && "content" in chunk) {
|
|
1511
|
-
const content = chunk.content;
|
|
1512
|
-
return typeof content === "string" ? content : JSON.stringify(content);
|
|
1513
|
-
}
|
|
1514
|
-
return JSON.stringify(chunk);
|
|
1515
|
-
}
|
|
1516
|
-
async function* streamLlm(context, request) {
|
|
1517
|
-
const chatFunction = context.llmProvider.chat;
|
|
1518
|
-
if (typeof chatFunction !== "function") {
|
|
1519
|
-
throw new Error(
|
|
1520
|
-
"LLM provider does not support legacy chat() method. Use AI SDK's streamText instead."
|
|
1521
|
-
);
|
|
1522
|
-
}
|
|
1523
|
-
const raw = chatFunction(request);
|
|
1524
|
-
let resolved = raw;
|
|
1525
|
-
if (resolved != null && typeof resolved.then === "function") {
|
|
1526
|
-
resolved = await resolved;
|
|
1527
|
-
}
|
|
1528
|
-
if (isAsyncIterable2(resolved)) {
|
|
1529
|
-
for await (const chunk of resolved) {
|
|
1530
|
-
yield chunk;
|
|
1531
|
-
}
|
|
1532
|
-
return;
|
|
1533
|
-
}
|
|
1534
|
-
yield resolved;
|
|
1535
|
-
}
|
|
1536
|
-
|
|
1537
|
-
// src/promptUtilities/utilities.ts
|
|
1538
|
-
function filterOldMessagesByDuration(messages, maxAgeMs, now = Date.now()) {
|
|
1539
|
-
if (maxAgeMs <= 0) return messages;
|
|
1540
|
-
const cutoff = now - maxAgeMs;
|
|
1541
|
-
return messages.filter((m) => typeof m.timestamp === "number" && m.timestamp >= cutoff);
|
|
1542
|
-
}
|
|
1543
|
-
|
|
1544
|
-
// src/loopAPI/agent-tool-loop/modelMessages.ts
|
|
1545
|
-
function chatMessageToModelMessage(message) {
|
|
1546
|
-
const role = message.role === "agent" || message.role === "error" ? "assistant" : message.role === "tool" ? "tool" : message.role === "user" ? "user" : "assistant";
|
|
1547
|
-
return {
|
|
1548
|
-
role,
|
|
1549
|
-
content: message.content
|
|
1550
|
-
};
|
|
1551
|
-
}
|
|
1552
|
-
async function resolveAgentDefinitionModel(context, definitionId) {
|
|
1553
|
-
if (context.resolveAgentDefinition) {
|
|
1554
|
-
return context.resolveAgentDefinition(definitionId);
|
|
1555
|
-
}
|
|
1556
|
-
return context.storage.getAgentDefinition(definitionId);
|
|
1557
|
-
}
|
|
1558
|
-
async function inferDefinitionId(storage, conversationId) {
|
|
1559
|
-
try {
|
|
1560
|
-
const meta = await storage.getConversationMeta(conversationId);
|
|
1561
|
-
if (meta?.definitionId) return meta.definitionId;
|
|
1562
|
-
} catch {
|
|
1563
|
-
}
|
|
1564
|
-
const parts = conversationId.split(":");
|
|
1565
|
-
if (parts.length >= 2) {
|
|
1566
|
-
return parts.slice(0, -1).join(":");
|
|
1567
|
-
}
|
|
1568
|
-
return conversationId;
|
|
1569
|
-
}
|
|
1570
|
-
async function buildLlmMessages(context, conversationId, history) {
|
|
1571
|
-
const definitionId = await inferDefinitionId(context.storage, conversationId);
|
|
1572
|
-
const definition = await resolveAgentDefinitionModel(context, definitionId);
|
|
1573
|
-
const fw = definition?.agentFrameworkConfig;
|
|
1574
|
-
const maxHistoryAgeMs = context.agentToolLoop?.maxHistoryAgeMs ?? 0;
|
|
1575
|
-
const historyForPrompt = maxHistoryAgeMs > 0 ? filterOldMessagesByDuration(history, maxHistoryAgeMs) : history;
|
|
1576
|
-
if (fw?.prompts && Array.isArray(fw.prompts) && fw.prompts.length > 0) {
|
|
1577
|
-
const readAttachmentFile = context.agentToolLoop?.readAttachmentFile;
|
|
1578
|
-
const gen = promptConcatStream(
|
|
1579
|
-
{
|
|
1580
|
-
agentFrameworkConfig: {
|
|
1581
|
-
prompts: fw.prompts,
|
|
1582
|
-
plugins: fw.plugins ?? [],
|
|
1583
|
-
response: []
|
|
1584
|
-
}
|
|
1585
|
-
},
|
|
1586
|
-
historyForPrompt,
|
|
1587
|
-
context,
|
|
1588
|
-
readAttachmentFile ? { readAttachmentFile } : void 0
|
|
1589
|
-
);
|
|
1590
|
-
let lastFlat = [];
|
|
1591
|
-
for await (const state of gen) {
|
|
1592
|
-
lastFlat = state.flatPrompts;
|
|
1593
|
-
}
|
|
1594
|
-
const withoutTrailingUser = lastFlat.length > 0 && lastFlat[lastFlat.length - 1]?.role === "user" ? lastFlat.slice(0, -1) : lastFlat;
|
|
1595
|
-
return [...withoutTrailingUser, ...historyForPrompt.map(chatMessageToModelMessage)];
|
|
1596
|
-
}
|
|
1597
|
-
const systemText = typeof definition?.systemPrompt === "string" ? definition.systemPrompt.trim() : "";
|
|
1598
|
-
if (systemText.length > 0) {
|
|
1599
|
-
return [
|
|
1600
|
-
{ role: "system", content: systemText },
|
|
1601
|
-
...historyForPrompt.map(chatMessageToModelMessage)
|
|
1602
|
-
];
|
|
1603
|
-
}
|
|
1604
|
-
return historyForPrompt.map(chatMessageToModelMessage);
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
// src/loopAPI/agent-tool-loop/toolResultMessage.ts
|
|
1608
|
-
function formatToolResultMessage(toolName, parameters, body, isError) {
|
|
1609
|
-
return `<functions_result>
|
|
1610
|
-
Tool: ${toolName}
|
|
1611
|
-
Parameters: ${JSON.stringify(parameters)}
|
|
1612
|
-
${isError ? "Error" : "Result"}: ${body}
|
|
1613
|
-
</functions_result>`;
|
|
1614
|
-
}
|
|
1615
|
-
|
|
1616
|
-
// src/loopAPI/agent-tool-loop/toolCallRunner.ts
|
|
1617
|
-
async function executeRegistryTool(context, toolId, parameters) {
|
|
1618
|
-
const normalizedId = toolId.includes("-") ? toolId.replace(/-([a-z])/g, (_m, c) => c.toUpperCase()) : toolId;
|
|
1619
|
-
const impl = context.tools.getTool(toolId) ?? context.tools.getTool(normalizedId);
|
|
1620
|
-
if (typeof impl !== "function") {
|
|
1621
|
-
return {
|
|
1622
|
-
text: `No tool registered for "${toolId}".`,
|
|
1623
|
-
isError: true
|
|
1624
|
-
};
|
|
1625
|
-
}
|
|
1626
|
-
try {
|
|
1627
|
-
const raw = await impl(parameters);
|
|
1628
|
-
if (raw != null && typeof raw === "object") {
|
|
1629
|
-
const o = raw;
|
|
1630
|
-
if (typeof o.error === "string" && o.error.length > 0) {
|
|
1631
|
-
return { text: o.error, isError: true };
|
|
1632
|
-
}
|
|
1633
|
-
if ("result" in o) {
|
|
1634
|
-
return {
|
|
1635
|
-
text: typeof o.result === "string" ? o.result : JSON.stringify(o.result),
|
|
1636
|
-
isError: false
|
|
1637
|
-
};
|
|
1638
|
-
}
|
|
1639
|
-
const structured = extractMemeloopStructuredToolPayload(raw);
|
|
1640
|
-
if (structured) {
|
|
1641
|
-
return {
|
|
1642
|
-
text: structured.summary,
|
|
1643
|
-
isError: false,
|
|
1644
|
-
detailRef: structured.detailRef,
|
|
1645
|
-
awaitSessionId: structured.awaitSessionId
|
|
1646
|
-
};
|
|
1647
|
-
}
|
|
1648
|
-
}
|
|
1649
|
-
return { text: typeof raw === "string" ? raw : JSON.stringify(raw), isError: false };
|
|
1650
|
-
} catch (error) {
|
|
1651
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1652
|
-
if (context.logger?.warn) {
|
|
1653
|
-
context.logger.warn("[agentToolLoop] tool execution error", toolId, message);
|
|
1654
|
-
} else {
|
|
1655
|
-
console.warn("[agentToolLoop] tool execution error", toolId, message);
|
|
1656
|
-
}
|
|
1657
|
-
return { text: message, isError: true };
|
|
1658
|
-
}
|
|
1659
|
-
}
|
|
1660
|
-
async function executeWithGuards(context, options, conversationId, recentToolCalls, call) {
|
|
1661
|
-
const signature = `${call.toolId}:${JSON.stringify(call.parameters)}`;
|
|
1662
|
-
recentToolCalls.push(signature);
|
|
1663
|
-
const threshold = Math.max(2, options?.doomLoopThreshold ?? 3);
|
|
1664
|
-
const last = recentToolCalls.slice(-threshold);
|
|
1665
|
-
if (last.length === threshold && last.every((x) => x === signature)) {
|
|
1666
|
-
return { text: "Blocked by doom-loop guard", isError: true };
|
|
1667
|
-
}
|
|
1668
|
-
const row = await executeRegistryTool(context, call.toolId, call.parameters);
|
|
1669
|
-
if (hasHooks("PostToolUse")) {
|
|
1670
|
-
await executeHooks("PostToolUse", context, {
|
|
1671
|
-
toolId: call.toolId,
|
|
1672
|
-
parameters: call.parameters,
|
|
1673
|
-
result: row.text,
|
|
1674
|
-
isError: row.isError,
|
|
1675
|
-
conversationId
|
|
1676
|
-
});
|
|
1677
|
-
}
|
|
1678
|
-
return row;
|
|
1679
|
-
}
|
|
1680
|
-
async function persistToolResult(context, conversationId, call, row) {
|
|
1681
|
-
const lamportTool = await nextLamportClockForConversation(context.storage, conversationId);
|
|
1682
|
-
await context.storage.appendMessage({
|
|
1683
|
-
messageId: `${conversationId}:t:${call.toolId}:${Date.now().toString(36)}`,
|
|
1684
|
-
conversationId,
|
|
1685
|
-
originNodeId: "local",
|
|
1686
|
-
timestamp: Date.now(),
|
|
1687
|
-
lamportClock: lamportTool,
|
|
1688
|
-
role: "tool",
|
|
1689
|
-
content: formatToolResultMessage(call.toolId, call.parameters, row.text, row.isError),
|
|
1690
|
-
detailRef: row.detailRef
|
|
1691
|
-
});
|
|
1692
|
-
}
|
|
1693
|
-
async function persistTerminalAwaitCompletion(context, options, conversationId, call, row) {
|
|
1694
|
-
const sid = row.awaitSessionId;
|
|
1695
|
-
const wait = options?.waitForTerminalSession;
|
|
1696
|
-
if (!sid || !wait || row.isError) return;
|
|
1697
|
-
const done = await wait(sid);
|
|
1698
|
-
const lamportTool = await nextLamportClockForConversation(context.storage, conversationId);
|
|
1699
|
-
const body = truncateToolSummary(
|
|
1700
|
-
`[terminal.await done] session=${sid}
|
|
1701
|
-
exitCode: ${done.exitCode ?? "null"}
|
|
1702
|
-
---
|
|
1703
|
-
${done.truncatedOutput}`
|
|
1704
|
-
);
|
|
1705
|
-
await context.storage.appendMessage({
|
|
1706
|
-
messageId: `${conversationId}:t:${call.toolId}:await:${Date.now().toString(36)}`,
|
|
1707
|
-
conversationId,
|
|
1708
|
-
originNodeId: "local",
|
|
1709
|
-
timestamp: Date.now(),
|
|
1710
|
-
lamportClock: lamportTool,
|
|
1711
|
-
role: "tool",
|
|
1712
|
-
content: formatToolResultMessage(call.toolId, call.parameters, body, false),
|
|
1713
|
-
detailRef: row.detailRef ? { ...row.detailRef, exitCode: done.exitCode ?? row.detailRef.exitCode } : void 0
|
|
1714
|
-
});
|
|
1715
|
-
}
|
|
1716
|
-
function toolStep(row, parallel) {
|
|
1717
|
-
return {
|
|
1718
|
-
type: "tool",
|
|
1719
|
-
data: {
|
|
1720
|
-
toolId: row.call.toolId,
|
|
1721
|
-
parameters: row.call.parameters,
|
|
1722
|
-
parallel,
|
|
1723
|
-
result: row.text,
|
|
1724
|
-
isError: row.isError
|
|
1725
|
-
}
|
|
1726
|
-
};
|
|
1727
|
-
}
|
|
1728
|
-
async function* runRegistryToolCalls(options) {
|
|
1729
|
-
const { context, agentToolLoopOptions, conversationId, calls, parallel, recentToolCalls } = options;
|
|
1730
|
-
if (parallel) {
|
|
1731
|
-
const results = await Promise.all(
|
|
1732
|
-
calls.map(
|
|
1733
|
-
async (call) => ({
|
|
1734
|
-
call,
|
|
1735
|
-
...await executeWithGuards(
|
|
1736
|
-
context,
|
|
1737
|
-
agentToolLoopOptions,
|
|
1738
|
-
conversationId,
|
|
1739
|
-
recentToolCalls,
|
|
1740
|
-
call
|
|
1741
|
-
)
|
|
1742
|
-
})
|
|
1743
|
-
)
|
|
1744
|
-
);
|
|
1745
|
-
for (const row of results) {
|
|
1746
|
-
yield toolStep(row, true);
|
|
1747
|
-
}
|
|
1748
|
-
for (const row of results) {
|
|
1749
|
-
await persistToolResult(context, conversationId, row.call, row);
|
|
1750
|
-
}
|
|
1751
|
-
for (const row of results) {
|
|
1752
|
-
await persistTerminalAwaitCompletion(
|
|
1753
|
-
context,
|
|
1754
|
-
agentToolLoopOptions,
|
|
1755
|
-
conversationId,
|
|
1756
|
-
row.call,
|
|
1757
|
-
row
|
|
1758
|
-
);
|
|
1759
|
-
}
|
|
1760
|
-
return;
|
|
1761
|
-
}
|
|
1762
|
-
for (const call of calls) {
|
|
1763
|
-
const row = await executeWithGuards(
|
|
1764
|
-
context,
|
|
1765
|
-
agentToolLoopOptions,
|
|
1766
|
-
conversationId,
|
|
1767
|
-
recentToolCalls,
|
|
1768
|
-
call
|
|
1769
|
-
);
|
|
1770
|
-
yield toolStep({ call, ...row }, false);
|
|
1771
|
-
await persistToolResult(context, conversationId, call, row);
|
|
1772
|
-
await persistTerminalAwaitCompletion(context, agentToolLoopOptions, conversationId, call, row);
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
|
|
1776
|
-
// src/loopAPI/agent-tool-loop/toolUseGate.ts
|
|
1777
|
-
function buildLayeredPermissions(options, definitionId, userSet) {
|
|
1778
|
-
const global = options?.toolPermissions;
|
|
1779
|
-
const sets = [];
|
|
1780
|
-
if (global?.default) {
|
|
1781
|
-
sets.push({
|
|
1782
|
-
source: "default",
|
|
1783
|
-
rules: [{ toolPattern: "*", action: global.default }]
|
|
1784
|
-
});
|
|
1785
|
-
}
|
|
1786
|
-
const scoped = global?.perAgent?.[definitionId];
|
|
1787
|
-
if (scoped) {
|
|
1788
|
-
if (scoped.default) {
|
|
1789
|
-
sets.push({
|
|
1790
|
-
source: `agent:${definitionId}:default`,
|
|
1791
|
-
rules: [{ toolPattern: "*", action: scoped.default }]
|
|
1792
|
-
});
|
|
1793
|
-
}
|
|
1794
|
-
if (scoped.rules && scoped.rules.length > 0) {
|
|
1795
|
-
sets.push({
|
|
1796
|
-
source: `agent:${definitionId}`,
|
|
1797
|
-
rules: scoped.rules.map((r) => ({ toolPattern: r.pattern, action: r.action }))
|
|
1798
|
-
});
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
if (userSet && userSet.rules.length > 0) {
|
|
1802
|
-
sets.push(userSet);
|
|
1803
|
-
}
|
|
1804
|
-
if (global?.rules && global.rules.length > 0) {
|
|
1805
|
-
sets.push({
|
|
1806
|
-
source: "session",
|
|
1807
|
-
rules: global.rules.map((r) => ({ toolPattern: r.pattern, action: r.action }))
|
|
1808
|
-
});
|
|
1809
|
-
}
|
|
1810
|
-
if (!sets.some((s) => s.rules.some((r) => r.toolPattern === "*"))) {
|
|
1811
|
-
sets.unshift({
|
|
1812
|
-
source: "implied-default",
|
|
1813
|
-
rules: [{ toolPattern: "*", action: "allow" }]
|
|
1814
|
-
});
|
|
1815
|
-
}
|
|
1816
|
-
return mergePermissionSets(sets);
|
|
1817
|
-
}
|
|
1818
|
-
function createPermissionPreToolUseHook(options, definitionId, userSet) {
|
|
1819
|
-
const mergedPermissions = buildLayeredPermissions(options, definitionId, userSet);
|
|
1820
|
-
return async (_context, data) => {
|
|
1821
|
-
const toolId = typeof data.toolId === "string" ? data.toolId : "";
|
|
1822
|
-
const action = checkPermission(toolId, mergedPermissions);
|
|
1823
|
-
if (action === "allow") {
|
|
1824
|
-
return { allowed: true, permissionAction: "allow" };
|
|
1825
|
-
}
|
|
1826
|
-
return {
|
|
1827
|
-
allowed: true,
|
|
1828
|
-
permissionAction: action,
|
|
1829
|
-
reason: action === "deny" ? "Denied by tool permission" : void 0
|
|
1830
|
-
};
|
|
1831
|
-
};
|
|
1832
|
-
}
|
|
1833
|
-
function applyModifiedCall(call, modified) {
|
|
1834
|
-
if (!modified) return call;
|
|
1835
|
-
const toolId = typeof modified.toolId === "string" ? modified.toolId : call.toolId;
|
|
1836
|
-
const parameters = modified.parameters != null && typeof modified.parameters === "object" ? modified.parameters : call.parameters;
|
|
1837
|
-
return { ...call, toolId, parameters };
|
|
1838
|
-
}
|
|
1839
|
-
function normalizePreToolUseResult(result) {
|
|
1840
|
-
if (!result.allowed) {
|
|
1841
|
-
return { action: "deny", reason: result.reason ?? "Blocked by PreToolUse hook" };
|
|
1842
|
-
}
|
|
1843
|
-
if (result.permissionAction === "ask" || result.permissionAction === "deny") {
|
|
1844
|
-
return { action: result.permissionAction, reason: result.reason };
|
|
1845
|
-
}
|
|
1846
|
-
return { action: "allow", reason: result.reason };
|
|
1847
|
-
}
|
|
1848
|
-
async function persistDeniedToolResult(context, conversationId, call, errorText) {
|
|
1849
|
-
const lamportTool = await nextLamportClockForConversation(context.storage, conversationId);
|
|
1850
|
-
await context.storage.appendMessage({
|
|
1851
|
-
messageId: `${conversationId}:t:${call.toolId}:${Date.now().toString(36)}`,
|
|
1852
|
-
conversationId,
|
|
1853
|
-
originNodeId: "local",
|
|
1854
|
-
timestamp: Date.now(),
|
|
1855
|
-
lamportClock: lamportTool,
|
|
1856
|
-
role: "tool",
|
|
1857
|
-
content: formatToolResultMessage(call.toolId, call.parameters, errorText, true)
|
|
1858
|
-
});
|
|
1859
|
-
}
|
|
1860
|
-
async function* resolveAskAction(conversationId, call) {
|
|
1861
|
-
yield {
|
|
1862
|
-
type: "permission_request",
|
|
1863
|
-
data: { tool: call.toolId, args: call.parameters }
|
|
1864
|
-
};
|
|
1865
|
-
const decision = await requestApproval(
|
|
1866
|
-
{
|
|
1867
|
-
approvalId: `${conversationId}:${Date.now().toString(36)}:${call.toolId}`,
|
|
1868
|
-
agentId: conversationId,
|
|
1869
|
-
toolName: call.toolId,
|
|
1870
|
-
parameters: call.parameters,
|
|
1871
|
-
created: /* @__PURE__ */ new Date()
|
|
1872
|
-
},
|
|
1873
|
-
6e4
|
|
1874
|
-
);
|
|
1875
|
-
return decision === "allow" ? "allow" : "deny";
|
|
1876
|
-
}
|
|
1877
|
-
async function runPreToolUseHook(context, data) {
|
|
1878
|
-
if (!hasHooks("PreToolUse")) return { allowed: true };
|
|
1879
|
-
return executeHooks("PreToolUse", context, data);
|
|
1880
|
-
}
|
|
1881
|
-
async function* gateToolCallsWithPreToolUse(context, options, definitionId, conversationId, calls) {
|
|
1882
|
-
const permissionHook = createPermissionPreToolUseHook(options, definitionId);
|
|
1883
|
-
const allowedCalls = [];
|
|
1884
|
-
for (const originalCall of calls) {
|
|
1885
|
-
let call = originalCall;
|
|
1886
|
-
const permissionResult = await permissionHook(context, {
|
|
1887
|
-
toolId: call.toolId,
|
|
1888
|
-
parameters: call.parameters,
|
|
1889
|
-
conversationId
|
|
1890
|
-
});
|
|
1891
|
-
call = applyModifiedCall(call, permissionResult.modified);
|
|
1892
|
-
let { action, reason } = normalizePreToolUseResult(permissionResult);
|
|
1893
|
-
if (action === "ask") {
|
|
1894
|
-
action = yield* resolveAskAction(conversationId, call);
|
|
1895
|
-
reason = action === "deny" ? "Tool approval denied or timed out" : reason;
|
|
1896
|
-
}
|
|
1897
|
-
if (action === "deny") {
|
|
1898
|
-
const errorText = reason ?? "Denied by tool permission";
|
|
1899
|
-
yield {
|
|
1900
|
-
type: "tool",
|
|
1901
|
-
data: {
|
|
1902
|
-
toolId: call.toolId,
|
|
1903
|
-
parameters: call.parameters,
|
|
1904
|
-
parallel: false,
|
|
1905
|
-
result: errorText,
|
|
1906
|
-
isError: true
|
|
1907
|
-
}
|
|
1908
|
-
};
|
|
1909
|
-
await persistDeniedToolResult(context, conversationId, call, errorText);
|
|
1910
|
-
continue;
|
|
1911
|
-
}
|
|
1912
|
-
const hookResult = await runPreToolUseHook(context, {
|
|
1913
|
-
toolId: call.toolId,
|
|
1914
|
-
parameters: call.parameters,
|
|
1915
|
-
conversationId
|
|
1916
|
-
});
|
|
1917
|
-
call = applyModifiedCall(call, hookResult.modified);
|
|
1918
|
-
({ action, reason } = normalizePreToolUseResult(hookResult));
|
|
1919
|
-
if (action === "ask") {
|
|
1920
|
-
action = yield* resolveAskAction(conversationId, call);
|
|
1921
|
-
reason = action === "deny" ? "Tool approval denied or timed out" : reason;
|
|
1922
|
-
}
|
|
1923
|
-
if (action === "deny") {
|
|
1924
|
-
const errorText = reason ?? "Blocked by PreToolUse hook";
|
|
1925
|
-
yield {
|
|
1926
|
-
type: "tool",
|
|
1927
|
-
data: {
|
|
1928
|
-
toolId: call.toolId,
|
|
1929
|
-
parameters: call.parameters,
|
|
1930
|
-
parallel: false,
|
|
1931
|
-
result: errorText,
|
|
1932
|
-
isError: true
|
|
1933
|
-
}
|
|
1934
|
-
};
|
|
1935
|
-
await persistDeniedToolResult(context, conversationId, call, errorText);
|
|
1936
|
-
continue;
|
|
1937
|
-
}
|
|
1938
|
-
allowedCalls.push(call);
|
|
1939
|
-
}
|
|
1940
|
-
return allowedCalls;
|
|
1941
|
-
}
|
|
1942
|
-
|
|
1943
|
-
// src/loopAPI/agent-tool-loop/turnPrimitives.ts
|
|
1944
|
-
var DEFAULT_MAX_ITERATIONS = 256;
|
|
1945
|
-
function toolCallHandledInAgentMessages(agentMessages, assistantContent, call) {
|
|
1946
|
-
let assistantIndex = -1;
|
|
1947
|
-
for (let index = agentMessages.length - 1; index >= 0; index -= 1) {
|
|
1948
|
-
const message = agentMessages[index];
|
|
1949
|
-
if (message.role === "assistant" && message.content === assistantContent) {
|
|
1950
|
-
assistantIndex = index;
|
|
1951
|
-
break;
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
if (assistantIndex < 0) return false;
|
|
1955
|
-
const after = agentMessages.slice(assistantIndex + 1);
|
|
1956
|
-
return after.some(
|
|
1957
|
-
(message) => message.role === "tool" && (message.metadata?.toolId === call.toolId || typeof message.content === "string" && message.content.includes(`Tool: ${call.toolId}`))
|
|
1958
|
-
);
|
|
1959
|
-
}
|
|
1960
|
-
function resolveMaxIterations(context) {
|
|
1961
|
-
const configured = context.agentToolLoop?.maxIterations;
|
|
1962
|
-
return configured != null && configured > 0 ? configured : DEFAULT_MAX_ITERATIONS;
|
|
1963
|
-
}
|
|
1964
|
-
function createAgentToolLoopState(context) {
|
|
1965
|
-
return {
|
|
1966
|
-
iteration: 0,
|
|
1967
|
-
maxIterations: resolveMaxIterations(context),
|
|
1968
|
-
recentToolCalls: [],
|
|
1969
|
-
agentStarted: false,
|
|
1970
|
-
agentStopped: false
|
|
1971
|
-
};
|
|
1972
|
-
}
|
|
1973
|
-
function markAgentToolLoopStop(state, reason) {
|
|
1974
|
-
state.stopReason ??= reason;
|
|
1975
|
-
}
|
|
1976
|
-
function finishAgentToolLoopThinking(state, reason, data) {
|
|
1977
|
-
markAgentToolLoopStop(state, reason);
|
|
1978
|
-
return { type: "thinking", data };
|
|
1979
|
-
}
|
|
1980
|
-
async function startAgentToolLoopTurn(context, input, state) {
|
|
1981
|
-
const now = Date.now();
|
|
1982
|
-
const lamportClock = await nextLamportClockForConversation(
|
|
1983
|
-
context.storage,
|
|
1984
|
-
input.conversationId
|
|
1985
|
-
);
|
|
1986
|
-
const hostUserMessage = input.userMessage;
|
|
1987
|
-
const userMessage = context.normalizeMessage?.({
|
|
1988
|
-
...hostUserMessage,
|
|
1989
|
-
messageId: hostUserMessage?.messageId ?? `${input.conversationId}:${now.toString(36)}`,
|
|
1990
|
-
conversationId: input.conversationId,
|
|
1991
|
-
originNodeId: hostUserMessage?.originNodeId ?? "local",
|
|
1992
|
-
timestamp: hostUserMessage?.timestamp ?? now,
|
|
1993
|
-
lamportClock: hostUserMessage?.lamportClock ?? lamportClock,
|
|
1994
|
-
role: "user",
|
|
1995
|
-
content: hostUserMessage?.content ?? input.message
|
|
1996
|
-
}) ?? {
|
|
1997
|
-
...hostUserMessage,
|
|
1998
|
-
messageId: hostUserMessage?.messageId ?? `${input.conversationId}:${now.toString(36)}`,
|
|
1999
|
-
conversationId: input.conversationId,
|
|
2000
|
-
originNodeId: hostUserMessage?.originNodeId ?? "local",
|
|
2001
|
-
timestamp: hostUserMessage?.timestamp ?? now,
|
|
2002
|
-
lamportClock: hostUserMessage?.lamportClock ?? lamportClock,
|
|
2003
|
-
role: "user",
|
|
2004
|
-
content: hostUserMessage?.content ?? input.message
|
|
2005
|
-
};
|
|
2006
|
-
if (input.resumeSession && input.resumeSession.length > 0) {
|
|
2007
|
-
await context.storage.insertMessagesIfAbsent(input.resumeSession);
|
|
2008
|
-
}
|
|
2009
|
-
await context.storage.appendMessage(userMessage);
|
|
2010
|
-
if (hasHooks("UserPromptSubmit")) {
|
|
2011
|
-
const hookResult = await executeHooks("UserPromptSubmit", context, {
|
|
2012
|
-
message: input.message,
|
|
2013
|
-
conversationId: input.conversationId
|
|
2014
|
-
});
|
|
2015
|
-
if (!hookResult.allowed) {
|
|
2016
|
-
return {
|
|
2017
|
-
action: "stop",
|
|
2018
|
-
step: {
|
|
2019
|
-
type: "thinking",
|
|
2020
|
-
data: {
|
|
2021
|
-
status: "blocked",
|
|
2022
|
-
conversationId: input.conversationId,
|
|
2023
|
-
reason: hookResult.reason ?? "Blocked by UserPromptSubmit hook"
|
|
2024
|
-
}
|
|
2025
|
-
}
|
|
2026
|
-
};
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
|
-
const initialDefinitionId = await inferDefinitionId(context.storage, input.conversationId);
|
|
2030
|
-
if (hasHooks("AgentStart")) {
|
|
2031
|
-
const hookResult = await executeHooks("AgentStart", context, {
|
|
2032
|
-
conversationId: input.conversationId,
|
|
2033
|
-
definitionId: initialDefinitionId
|
|
2034
|
-
});
|
|
2035
|
-
if (!hookResult.allowed) {
|
|
2036
|
-
return {
|
|
2037
|
-
action: "stop",
|
|
2038
|
-
step: {
|
|
2039
|
-
type: "thinking",
|
|
2040
|
-
data: {
|
|
2041
|
-
status: "blocked",
|
|
2042
|
-
conversationId: input.conversationId,
|
|
2043
|
-
reason: hookResult.reason ?? "Blocked by AgentStart hook"
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
};
|
|
2047
|
-
}
|
|
2048
|
-
}
|
|
2049
|
-
state.agentStarted = true;
|
|
2050
|
-
return { action: "continue" };
|
|
2051
|
-
}
|
|
2052
|
-
async function* runAgentToolLoopIteration(context, input, state) {
|
|
2053
|
-
const options = context.agentToolLoop ?? {};
|
|
2054
|
-
const enableToolLoop = options.enableToolLoop !== false;
|
|
2055
|
-
const fallbackRegistry = options.fallbackRegistryTools !== false;
|
|
2056
|
-
const checkpointOptions = options.sessionCheckpoint;
|
|
2057
|
-
if (state.iteration >= state.maxIterations) {
|
|
2058
|
-
yield finishAgentToolLoopThinking(state, "max-iterations", {
|
|
2059
|
-
status: "max-iterations",
|
|
2060
|
-
conversationId: input.conversationId,
|
|
2061
|
-
maxIterations: state.maxIterations
|
|
2062
|
-
});
|
|
2063
|
-
return { action: "stop", reason: "max-iterations" };
|
|
2064
|
-
}
|
|
2065
|
-
state.iteration += 1;
|
|
2066
|
-
const iteration = state.iteration;
|
|
2067
|
-
if (options.isCancelled?.(input.conversationId)) {
|
|
2068
|
-
yield finishAgentToolLoopThinking(state, "cancelled", {
|
|
2069
|
-
status: "cancelled",
|
|
2070
|
-
conversationId: input.conversationId
|
|
2071
|
-
});
|
|
2072
|
-
return { action: "stop", reason: "cancelled" };
|
|
2073
|
-
}
|
|
2074
|
-
const rawHistory = await context.storage.getMessages(input.conversationId, {
|
|
2075
|
-
mode: "full-content"
|
|
2076
|
-
});
|
|
2077
|
-
const { history, steps: compactionSteps } = await prepareIterationHistory({
|
|
2078
|
-
context,
|
|
2079
|
-
conversationId: input.conversationId,
|
|
2080
|
-
iteration,
|
|
2081
|
-
rawHistory,
|
|
2082
|
-
agentToolLoopOptions: options
|
|
2083
|
-
});
|
|
2084
|
-
for (const step of compactionSteps) {
|
|
2085
|
-
yield step;
|
|
2086
|
-
}
|
|
2087
|
-
const runtimeAgent = context.resolveAgentRuntimeView ? await context.resolveAgentRuntimeView(input.conversationId, history) : { id: input.conversationId, messages: history };
|
|
2088
|
-
const hookContext = {
|
|
2089
|
-
...context,
|
|
2090
|
-
agent: runtimeAgent,
|
|
2091
|
-
persistAgentMessage: async (message) => {
|
|
2092
|
-
await context.storage.appendMessage(message);
|
|
2093
|
-
}
|
|
2094
|
-
};
|
|
2095
|
-
yield {
|
|
2096
|
-
type: "thinking",
|
|
2097
|
-
data: {
|
|
2098
|
-
status: "calling-llm",
|
|
2099
|
-
conversationId: input.conversationId,
|
|
2100
|
-
messageCount: history.length,
|
|
2101
|
-
iteration
|
|
2102
|
-
}
|
|
2103
|
-
};
|
|
2104
|
-
const messages = await buildLlmMessages(context, input.conversationId, history);
|
|
2105
|
-
const request = { conversationId: input.conversationId, messages };
|
|
2106
|
-
let assistantText = "";
|
|
2107
|
-
for await (const chunk of streamLlm(context, request)) {
|
|
2108
|
-
assistantText += chunkToText2(chunk);
|
|
2109
|
-
yield { type: "message", data: chunk };
|
|
2110
|
-
}
|
|
2111
|
-
const definitionId = await inferDefinitionId(context.storage, input.conversationId);
|
|
2112
|
-
const agentDefinition = await resolveAgentDefinitionModel(context, definitionId);
|
|
2113
|
-
const frameworkConfig = agentDefinition?.agentFrameworkConfig;
|
|
2114
|
-
const hasPlugins = Boolean(
|
|
2115
|
-
frameworkConfig?.plugins && Array.isArray(frameworkConfig.plugins) && frameworkConfig.plugins.length > 0
|
|
2116
|
-
);
|
|
2117
|
-
const assistantMessage = context.normalizeMessage?.({
|
|
2118
|
-
messageId: `${input.conversationId}:a:${Date.now().toString(36)}`,
|
|
2119
|
-
conversationId: input.conversationId,
|
|
2120
|
-
originNodeId: "local",
|
|
2121
|
-
timestamp: Date.now(),
|
|
2122
|
-
lamportClock: await nextLamportClockForConversation(
|
|
2123
|
-
context.storage,
|
|
2124
|
-
input.conversationId
|
|
2125
|
-
),
|
|
2126
|
-
role: "assistant",
|
|
2127
|
-
content: assistantText
|
|
2128
|
-
}) ?? {
|
|
2129
|
-
messageId: `${input.conversationId}:a:${Date.now().toString(36)}`,
|
|
2130
|
-
conversationId: input.conversationId,
|
|
2131
|
-
originNodeId: "local",
|
|
2132
|
-
timestamp: Date.now(),
|
|
2133
|
-
lamportClock: await nextLamportClockForConversation(
|
|
2134
|
-
context.storage,
|
|
2135
|
-
input.conversationId
|
|
2136
|
-
),
|
|
2137
|
-
role: "assistant",
|
|
2138
|
-
content: assistantText
|
|
2139
|
-
};
|
|
2140
|
-
hookContext.agent.messages.push(assistantMessage);
|
|
2141
|
-
await hookContext.persistAgentMessage?.(assistantMessage);
|
|
2142
|
-
const { calls, parallel } = matchAllToolCallings(assistantText);
|
|
2143
|
-
if (hasPlugins && frameworkConfig) {
|
|
2144
|
-
const { hooks } = await createHooksWithPlugins(
|
|
2145
|
-
frameworkConfig,
|
|
2146
|
-
{
|
|
2147
|
-
pluginRegistry: resolvePromptPluginMap(context)
|
|
2148
|
-
}
|
|
2149
|
-
);
|
|
2150
|
-
const responseCompletePayload = {
|
|
2151
|
-
agentFrameworkContext: hookContext,
|
|
2152
|
-
response: { status: "done", content: assistantText },
|
|
2153
|
-
agentFrameworkConfig: frameworkConfig,
|
|
2154
|
-
requestId: void 0,
|
|
2155
|
-
toolConfig: { id: "_memeloop", toolId: "_memeloop" },
|
|
2156
|
-
actions: {}
|
|
2157
|
-
};
|
|
2158
|
-
await runResponseCompleteHooks(hooks, responseCompletePayload);
|
|
2159
|
-
await context.storage.insertMessagesIfAbsent(hookContext.agent.messages);
|
|
2160
|
-
const postProcess = await responseConcat(
|
|
2161
|
-
frameworkConfig,
|
|
2162
|
-
assistantText,
|
|
2163
|
-
hookContext,
|
|
2164
|
-
hookContext.agent.messages
|
|
2165
|
-
);
|
|
2166
|
-
const yieldTarget = responseCompletePayload.actions?.yieldNextRoundTo ?? postProcess.yieldNextRoundTo;
|
|
2167
|
-
if (yieldTarget === "human") {
|
|
2168
|
-
yield finishAgentToolLoopThinking(state, "completed", {
|
|
2169
|
-
status: "input-required",
|
|
2170
|
-
conversationId: input.conversationId
|
|
2171
|
-
});
|
|
2172
|
-
return { action: "stop", reason: "completed" };
|
|
2173
|
-
}
|
|
2174
|
-
if (yieldTarget === "self") {
|
|
2175
|
-
return { action: "continue" };
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2178
|
-
if (calls.length === 0) {
|
|
2179
|
-
markAgentToolLoopStop(state, "completed");
|
|
2180
|
-
return { action: "stop", reason: "completed" };
|
|
2181
|
-
}
|
|
2182
|
-
if (!enableToolLoop) {
|
|
2183
|
-
markAgentToolLoopStop(state, "completed");
|
|
2184
|
-
return { action: "stop", reason: "completed" };
|
|
2185
|
-
}
|
|
2186
|
-
const pending2 = calls.filter(
|
|
2187
|
-
(call) => !toolCallHandledInAgentMessages(hookContext.agent.messages, assistantText, call)
|
|
2188
|
-
);
|
|
2189
|
-
if (pending2.length === 0) {
|
|
2190
|
-
if (hasPlugins && calls.length > 0) {
|
|
2191
|
-
return { action: "continue" };
|
|
2192
|
-
}
|
|
2193
|
-
markAgentToolLoopStop(state, "completed");
|
|
2194
|
-
return { action: "stop", reason: "completed" };
|
|
2195
|
-
}
|
|
2196
|
-
if (!fallbackRegistry && hasPlugins) {
|
|
2197
|
-
return { action: "continue" };
|
|
2198
|
-
}
|
|
2199
|
-
const allowedCalls = yield* gateToolCallsWithPreToolUse(
|
|
2200
|
-
context,
|
|
2201
|
-
options,
|
|
2202
|
-
definitionId,
|
|
2203
|
-
input.conversationId,
|
|
2204
|
-
pending2
|
|
2205
|
-
);
|
|
2206
|
-
if (allowedCalls.length === 0) {
|
|
2207
|
-
return { action: "continue" };
|
|
2208
|
-
}
|
|
2209
|
-
yield* runRegistryToolCalls({
|
|
2210
|
-
context,
|
|
2211
|
-
agentToolLoopOptions: options,
|
|
2212
|
-
conversationId: input.conversationId,
|
|
2213
|
-
calls: allowedCalls,
|
|
2214
|
-
parallel,
|
|
2215
|
-
recentToolCalls: state.recentToolCalls
|
|
2216
|
-
});
|
|
2217
|
-
if (checkpointOptions?.enabled) {
|
|
2218
|
-
const checkpointStore = checkpointOptions.store;
|
|
2219
|
-
if (!checkpointStore) {
|
|
2220
|
-
if (context.logger?.warn) {
|
|
2221
|
-
context.logger.warn("[agentToolLoop] checkpoint enabled without a checkpoint store");
|
|
2222
|
-
} else {
|
|
2223
|
-
console.warn("[agentToolLoop] checkpoint enabled without a checkpoint store");
|
|
2224
|
-
}
|
|
2225
|
-
return { action: "continue" };
|
|
2226
|
-
}
|
|
2227
|
-
try {
|
|
2228
|
-
const allMessages = await context.storage.getMessages(input.conversationId, {
|
|
2229
|
-
mode: "full-content"
|
|
2230
|
-
});
|
|
2231
|
-
await checkpointStore.saveCheckpoint(input.conversationId, allMessages);
|
|
2232
|
-
} catch (error) {
|
|
2233
|
-
if (context.logger?.warn) {
|
|
2234
|
-
context.logger.warn("[agentToolLoop] checkpoint save failed:", error);
|
|
2235
|
-
} else {
|
|
2236
|
-
console.warn("[agentToolLoop] checkpoint save failed:", error);
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
return { action: "continue" };
|
|
2241
|
-
}
|
|
2242
|
-
async function stopAgentToolLoopTurn(context, input, state, reason) {
|
|
2243
|
-
if (reason) markAgentToolLoopStop(state, reason);
|
|
2244
|
-
if (state.agentStarted && !state.agentStopped && state.stopReason && hasHooks("AgentStop")) {
|
|
2245
|
-
state.agentStopped = true;
|
|
2246
|
-
await executeHooks("AgentStop", context, {
|
|
2247
|
-
conversationId: input.conversationId,
|
|
2248
|
-
reason: state.stopReason
|
|
2249
|
-
});
|
|
2250
|
-
}
|
|
2251
|
-
}
|
|
2252
|
-
|
|
2253
|
-
// src/loopAPI/agent-tool-loop/scriptRunner.ts
|
|
2254
|
-
function isAsyncIterable3(value) {
|
|
2255
|
-
return Boolean(value && typeof value === "object" && Symbol.asyncIterator in value);
|
|
2256
|
-
}
|
|
2257
|
-
function messageStep2(message) {
|
|
2258
|
-
return { type: "message", data: message };
|
|
2259
|
-
}
|
|
2260
|
-
async function* drainEmittedSteps2(steps) {
|
|
2261
|
-
while (steps.length > 0) {
|
|
2262
|
-
const step = steps.shift();
|
|
2263
|
-
if (step) yield step;
|
|
2264
|
-
}
|
|
2265
|
-
}
|
|
2266
|
-
function asAgentToolLoopContext(rawContext) {
|
|
2267
|
-
return rawContext;
|
|
2268
|
-
}
|
|
2269
|
-
async function resolveAgentToolLoopScript(context) {
|
|
2270
|
-
if (context.script) return context.script;
|
|
2271
|
-
const scriptReference = context.profile?.scriptReference ?? context.profile?.scriptRef ?? context.profile?.script;
|
|
2272
|
-
if (!scriptReference) return void 0;
|
|
2273
|
-
if (context.loadScript) return context.loadScript(scriptReference, context);
|
|
2274
|
-
return loadAgentToolLoopScript(scriptReference, context.scriptPolicy);
|
|
2275
|
-
}
|
|
2276
|
-
function createAgentToolLoopScriptContext(input, context, emittedSteps) {
|
|
2277
|
-
const emit = (step) => {
|
|
2278
|
-
emittedSteps.push(step);
|
|
2279
|
-
context.runtime?.emit?.(step);
|
|
2280
|
-
};
|
|
2281
|
-
return {
|
|
2282
|
-
input,
|
|
2283
|
-
context,
|
|
2284
|
-
profile: context.profile,
|
|
2285
|
-
createState: () => createAgentToolLoopState(context),
|
|
2286
|
-
startTurn: (state) => startAgentToolLoopTurn(context, input, state),
|
|
2287
|
-
runIteration: (state) => runAgentToolLoopIteration(context, input, state),
|
|
2288
|
-
stopTurn: (state, reason) => stopAgentToolLoopTurn(context, input, state, reason),
|
|
2289
|
-
emit,
|
|
2290
|
-
finish: (message) => {
|
|
2291
|
-
emit(typeof message === "string" ? messageStep2(message) : message);
|
|
2292
|
-
},
|
|
2293
|
-
isCancelled: () => context.runtime?.signal?.cancelled === true || context.agentToolLoop?.isCancelled?.(input.conversationId) === true,
|
|
2294
|
-
log: (event, data) => {
|
|
2295
|
-
context.runtime?.log?.(event, data);
|
|
2296
|
-
context.logger?.debug?.(event, data);
|
|
2297
|
-
}
|
|
2298
|
-
};
|
|
2299
|
-
}
|
|
2300
|
-
async function* runAgentToolLoopScript(script, input, context) {
|
|
2301
|
-
const emittedSteps = [];
|
|
2302
|
-
const result = await script(createAgentToolLoopScriptContext(input, context, emittedSteps));
|
|
2303
|
-
yield* drainEmittedSteps2(emittedSteps);
|
|
2304
|
-
if (isAsyncIterable3(result)) {
|
|
2305
|
-
yield* result;
|
|
2306
|
-
yield* drainEmittedSteps2(emittedSteps);
|
|
2307
|
-
} else if (typeof result === "string") {
|
|
2308
|
-
yield messageStep2(result);
|
|
2309
|
-
} else if (Array.isArray(result)) {
|
|
2310
|
-
yield* result;
|
|
2311
|
-
} else if (result) {
|
|
2312
|
-
yield result;
|
|
2313
|
-
}
|
|
2314
|
-
}
|
|
2315
|
-
|
|
2316
|
-
// src/loopAPI/agent-tool-loop/loop.ts
|
|
2317
|
-
function createAgentToolLoopRunner(context) {
|
|
2318
|
-
return async function* agentToolLoopRunner(input) {
|
|
2319
|
-
const state = createAgentToolLoopState(context);
|
|
2320
|
-
try {
|
|
2321
|
-
const start = await startAgentToolLoopTurn(context, input, state);
|
|
2322
|
-
if (start.step) yield start.step;
|
|
2323
|
-
if (start.action === "stop") return;
|
|
2324
|
-
while (true) {
|
|
2325
|
-
const result = yield* runAgentToolLoopIteration(context, input, state);
|
|
2326
|
-
if (result.action === "stop") return;
|
|
2327
|
-
}
|
|
2328
|
-
} catch (error) {
|
|
2329
|
-
await stopAgentToolLoopTurn(context, input, state, "error");
|
|
2330
|
-
throw error;
|
|
2331
|
-
} finally {
|
|
2332
|
-
await stopAgentToolLoopTurn(context, input, state);
|
|
2333
|
-
}
|
|
2334
|
-
};
|
|
2335
|
-
}
|
|
2336
|
-
function createAgentToolLoopDefinition(name = "AgentToolLoop") {
|
|
2337
|
-
return {
|
|
2338
|
-
id: "agent-tool-loop",
|
|
2339
|
-
name,
|
|
2340
|
-
description: "Agent/tool loop that alternates between LLM output and external tool execution.",
|
|
2341
|
-
createRunner: (rawContext) => {
|
|
2342
|
-
const context = asAgentToolLoopContext(rawContext);
|
|
2343
|
-
return async function* agentToolLoop(input) {
|
|
2344
|
-
const script = await resolveAgentToolLoopScript(context);
|
|
2345
|
-
if (script) {
|
|
2346
|
-
yield* runAgentToolLoopScript(script, input, context);
|
|
2347
|
-
return;
|
|
2348
|
-
}
|
|
2349
|
-
yield* createAgentToolLoopRunner(context)(input);
|
|
2350
|
-
};
|
|
2351
|
-
}
|
|
2352
|
-
};
|
|
2353
|
-
}
|
|
2354
|
-
|
|
2355
|
-
// src/loopAPI/agent-tool-loop/runner.ts
|
|
2356
|
-
function resolveAgentToolLoopTerminalState(step, current) {
|
|
2357
|
-
if (step.type !== "thinking") return current;
|
|
2358
|
-
const data = step.data;
|
|
2359
|
-
if (data.status === "input-required") return "input-required";
|
|
2360
|
-
if (data.status === "cancelled") return "canceled";
|
|
2361
|
-
if (data.status === "max-iterations") return "completed";
|
|
2362
|
-
if (data.status === "blocked") return "failed";
|
|
2363
|
-
if (data.status === "calling-llm") return "working";
|
|
2364
|
-
return current;
|
|
2365
|
-
}
|
|
2366
|
-
async function runAgentToolLoopTurn(context, input, callbacks = {}) {
|
|
2367
|
-
const agentToolLoop = callbacks.agentToolLoop ?? createAgentToolLoopRunner(context);
|
|
2368
|
-
let terminalState = "completed";
|
|
2369
|
-
let stepCount = 0;
|
|
2370
|
-
for await (const step of agentToolLoop(input)) {
|
|
2371
|
-
stepCount += 1;
|
|
2372
|
-
terminalState = resolveAgentToolLoopTerminalState(step, terminalState);
|
|
2373
|
-
await callbacks.onStep?.(step);
|
|
2374
|
-
if (step.type === "thinking" && step.data && typeof step.data === "object") {
|
|
2375
|
-
const data = step.data;
|
|
2376
|
-
const status = data.status;
|
|
2377
|
-
if (typeof status === "string") {
|
|
2378
|
-
await callbacks.onProgress?.(status, data, step);
|
|
2379
|
-
}
|
|
2380
|
-
}
|
|
2381
|
-
}
|
|
2382
|
-
return {
|
|
2383
|
-
state: terminalState === "working" || terminalState === "submitted" ? "completed" : terminalState,
|
|
2384
|
-
stepCount
|
|
2385
|
-
};
|
|
2386
|
-
}
|
|
2387
|
-
|
|
2388
|
-
// src/loopAPI/registry.ts
|
|
2389
|
-
function normalizePluginSelections(selected) {
|
|
2390
|
-
if (!selected) return void 0;
|
|
2391
|
-
const result = /* @__PURE__ */ new Map();
|
|
2392
|
-
for (const entry of selected) {
|
|
2393
|
-
if (typeof entry === "string") {
|
|
2394
|
-
result.set(entry, { id: entry, enabled: true });
|
|
2395
|
-
continue;
|
|
2396
|
-
}
|
|
2397
|
-
if (entry.enabled === false) continue;
|
|
2398
|
-
result.set(entry.id, entry);
|
|
2399
|
-
}
|
|
2400
|
-
return result;
|
|
2401
|
-
}
|
|
2402
|
-
var LoopRegistryImpl = class {
|
|
2403
|
-
loops = /* @__PURE__ */ new Map();
|
|
2404
|
-
profiles = /* @__PURE__ */ new Map();
|
|
2405
|
-
plugins = /* @__PURE__ */ new Map();
|
|
2406
|
-
// ── Loop registration ──
|
|
2407
|
-
registerLoop(definition) {
|
|
2408
|
-
if (!definition.id) throw new Error("Loop definition must have an id");
|
|
2409
|
-
this.loops.set(definition.id, definition);
|
|
2410
|
-
}
|
|
2411
|
-
getLoop(id) {
|
|
2412
|
-
return this.loops.get(id);
|
|
2413
|
-
}
|
|
2414
|
-
listLoops() {
|
|
2415
|
-
return Array.from(this.loops.values());
|
|
2416
|
-
}
|
|
2417
|
-
// ── Profile registration ──
|
|
2418
|
-
registerProfile(profile) {
|
|
2419
|
-
if (!profile.id) throw new Error("Loop profile must have an id");
|
|
2420
|
-
this.profiles.set(profile.id, profile);
|
|
2421
|
-
}
|
|
2422
|
-
getProfile(id) {
|
|
2423
|
-
return this.profiles.get(id);
|
|
2424
|
-
}
|
|
2425
|
-
listProfiles() {
|
|
2426
|
-
return Array.from(this.profiles.values());
|
|
2427
|
-
}
|
|
2428
|
-
// ── Plugin registration ──
|
|
2429
|
-
registerPlugin(plugin) {
|
|
2430
|
-
if (!plugin.id) throw new Error("Loop plugin must have an id");
|
|
2431
|
-
this.plugins.set(plugin.id, plugin);
|
|
2432
|
-
}
|
|
2433
|
-
getPlugin(id) {
|
|
2434
|
-
return this.plugins.get(id);
|
|
2435
|
-
}
|
|
2436
|
-
listPlugins() {
|
|
2437
|
-
return Array.from(this.plugins.values());
|
|
2438
|
-
}
|
|
2439
|
-
installPluginsForLoop(loopId, target, selected) {
|
|
2440
|
-
const selectedEntries = normalizePluginSelections(selected);
|
|
2441
|
-
for (const plugin of this.plugins.values()) {
|
|
2442
|
-
const entry = selectedEntries?.get(plugin.id);
|
|
2443
|
-
if (selectedEntries && !entry) continue;
|
|
2444
|
-
if (plugin.targetLoopId && plugin.targetLoopId !== "*" && plugin.targetLoopId !== loopId) {
|
|
2445
|
-
continue;
|
|
2446
|
-
}
|
|
2447
|
-
if (plugin.install) {
|
|
2448
|
-
plugin.install(target, entry?.config);
|
|
2449
|
-
}
|
|
2450
|
-
}
|
|
2451
|
-
}
|
|
2452
|
-
installPluginsForProfile(profile, target) {
|
|
2453
|
-
this.installPluginsForLoop(profile.loopId ?? "agent-tool-loop", target, profile.plugins ?? []);
|
|
2454
|
-
}
|
|
2455
|
-
createRunnerForProfile(profile, context = {}) {
|
|
2456
|
-
const loopId = profile.loopId ?? "agent-tool-loop";
|
|
2457
|
-
this.installPluginsForProfile(profile, context);
|
|
2458
|
-
return this.createRunner(loopId, { ...context, profile });
|
|
2459
|
-
}
|
|
2460
|
-
// ── Lifecycle ──
|
|
2461
|
-
createRunner(loopId, context = {}) {
|
|
2462
|
-
const definition = this.loops.get(loopId);
|
|
2463
|
-
if (!definition) return null;
|
|
2464
|
-
return definition.createRunner(context);
|
|
2465
|
-
}
|
|
2466
|
-
reset() {
|
|
2467
|
-
this.loops.clear();
|
|
2468
|
-
this.profiles.clear();
|
|
2469
|
-
this.plugins.clear();
|
|
2470
|
-
}
|
|
2471
|
-
};
|
|
2472
|
-
var defaultRegistry = null;
|
|
2473
|
-
function getLoopRegistry() {
|
|
2474
|
-
if (!defaultRegistry) {
|
|
2475
|
-
defaultRegistry = new LoopRegistryImpl();
|
|
2476
|
-
}
|
|
2477
|
-
return defaultRegistry;
|
|
2478
|
-
}
|
|
2479
|
-
function resetLoopRegistry() {
|
|
2480
|
-
if (defaultRegistry) {
|
|
2481
|
-
defaultRegistry.reset();
|
|
2482
|
-
}
|
|
2483
|
-
defaultRegistry = null;
|
|
2484
|
-
}
|
|
2485
|
-
|
|
2486
|
-
// src/loopAPI/plugins/builtinLoopsPlugin.ts
|
|
2487
|
-
var AGENT_TOOL_LOOP_ID = "agent-tool-loop";
|
|
2488
|
-
var AGENT_AGENT_LOOP_ID = getAgentAgentLoopId();
|
|
2489
|
-
function registerBuiltinLoops() {
|
|
2490
|
-
const registry = getLoopRegistry();
|
|
2491
|
-
if (!registry.getLoop(AGENT_TOOL_LOOP_ID)) {
|
|
2492
|
-
registry.registerLoop(createAgentToolLoopDefinition("AgentToolLoop"));
|
|
2493
|
-
}
|
|
2494
|
-
if (!registry.getLoop(AGENT_AGENT_LOOP_ID)) {
|
|
2495
|
-
registry.registerLoop(createAgentAgentLoopDefinition());
|
|
2496
|
-
}
|
|
2497
|
-
}
|
|
2498
|
-
|
|
2499
|
-
// src/tools/schemaRegistry.ts
|
|
2500
|
-
var toolSchemas = /* @__PURE__ */ new Map();
|
|
2501
|
-
var toolMetadata = /* @__PURE__ */ new Map();
|
|
2502
|
-
function registerToolParameterSchema(toolId, schema, metadata) {
|
|
2503
|
-
toolSchemas.set(toolId, schema);
|
|
2504
|
-
if (metadata) {
|
|
2505
|
-
toolMetadata.set(toolId, metadata);
|
|
2506
|
-
}
|
|
2507
|
-
}
|
|
2508
|
-
function getToolParameterSchema(toolId) {
|
|
2509
|
-
return toolSchemas.get(toolId);
|
|
2510
|
-
}
|
|
2511
|
-
function getToolMetadata(toolId) {
|
|
2512
|
-
return toolMetadata.get(toolId);
|
|
2513
|
-
}
|
|
2514
|
-
|
|
2515
|
-
// src/tools/builtins/questionWaitRegistry.ts
|
|
2516
|
-
var pending = /* @__PURE__ */ new Map();
|
|
2517
|
-
function resolveQuestionAnswer(questionId, answer) {
|
|
2518
|
-
const p = pending.get(questionId);
|
|
2519
|
-
if (!p) return false;
|
|
2520
|
-
clearTimeout(p.timer);
|
|
2521
|
-
pending.delete(questionId);
|
|
2522
|
-
p.resolve(answer);
|
|
2523
|
-
return true;
|
|
2524
|
-
}
|
|
2525
|
-
function waitForQuestionAnswer(questionId, timeoutMs) {
|
|
2526
|
-
return new Promise((resolve, reject) => {
|
|
2527
|
-
const timer = setTimeout(() => {
|
|
2528
|
-
pending.delete(questionId);
|
|
2529
|
-
reject(new Error("askQuestion_timeout"));
|
|
2530
|
-
}, timeoutMs);
|
|
2531
|
-
pending.set(questionId, { resolve, reject, timer });
|
|
2532
|
-
});
|
|
2533
|
-
}
|
|
2534
|
-
|
|
2535
|
-
// src/tools/builtins/askQuestion.ts
|
|
2536
|
-
import { z } from "zod";
|
|
2537
|
-
var askQuestionConfigSchema = z.object({
|
|
2538
|
-
question: z.string().min(1),
|
|
2539
|
-
/**
|
|
2540
|
-
* Desktop / TidGi 的 LLM 工具参数里通常不会显式携带 conversationId,
|
|
2541
|
-
* 由宿主侧上下文负责关联;这里允许缺省,交给宿主回调侧做路由。
|
|
2542
|
-
*/
|
|
2543
|
-
conversationId: z.string().min(1).optional(),
|
|
2544
|
-
timeoutMs: z.number().int().positive().max(36e5).optional(),
|
|
2545
|
-
inputType: z.enum(["single-select", "multi-select", "text"]).optional(),
|
|
2546
|
-
options: z.array(
|
|
2547
|
-
z.object({
|
|
2548
|
-
label: z.string(),
|
|
2549
|
-
description: z.string().optional()
|
|
2550
|
-
})
|
|
2551
|
-
).optional(),
|
|
2552
|
-
allowFreeform: z.boolean().optional().default(true)
|
|
2553
|
-
});
|
|
2554
|
-
var ASK_QUESTION_TOOL_ID = "ask-question";
|
|
2555
|
-
async function askQuestionImpl(arguments_, context) {
|
|
2556
|
-
const parsed = askQuestionConfigSchema.safeParse(arguments_);
|
|
2557
|
-
if (!parsed.success) {
|
|
2558
|
-
return { error: "invalid_askQuestion_args" };
|
|
2559
|
-
}
|
|
2560
|
-
const { question, conversationId, timeoutMs, inputType, options, allowFreeform } = parsed.data;
|
|
2561
|
-
const questionId = crypto.randomUUID();
|
|
2562
|
-
const timeout = timeoutMs ?? 3e5;
|
|
2563
|
-
context.notifyAskQuestion?.({
|
|
2564
|
-
questionId,
|
|
2565
|
-
question,
|
|
2566
|
-
conversationId,
|
|
2567
|
-
inputType,
|
|
2568
|
-
options,
|
|
2569
|
-
allowFreeform
|
|
2570
|
-
});
|
|
2571
|
-
try {
|
|
2572
|
-
const answer = await waitForQuestionAnswer(questionId, timeout);
|
|
2573
|
-
return { result: answer };
|
|
2574
|
-
} catch (error) {
|
|
2575
|
-
return { error: error instanceof Error ? error.message : "askQuestion_failed" };
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
|
|
2579
|
-
// src/tools/builtins/askUserQuestion.ts
|
|
2580
|
-
import { z as z2 } from "zod";
|
|
2581
|
-
var askUserQuestionConfigSchema = z2.object({
|
|
2582
|
-
question: z2.string().min(1).describe("The question to ask the user"),
|
|
2583
|
-
inputType: z2.enum(["text", "single-select", "multi-select"]).optional().default("text").describe("Type of input expected: text, single-select, or multi-select"),
|
|
2584
|
-
options: z2.array(
|
|
2585
|
-
z2.object({
|
|
2586
|
-
label: z2.string().min(1),
|
|
2587
|
-
description: z2.string().optional()
|
|
2588
|
-
})
|
|
2589
|
-
).optional().describe("Options for single-select or multi-select input types"),
|
|
2590
|
-
allowFreeform: z2.boolean().optional().default(true).describe("Allow free-text input even for select types"),
|
|
2591
|
-
timeoutMs: z2.number().int().positive().max(36e5).optional().default(3e5).describe("Timeout in milliseconds (default 5 minutes)")
|
|
2592
|
-
});
|
|
2593
|
-
var ASK_USER_QUESTION_TOOL_ID = "askUserQuestion";
|
|
2594
|
-
async function askUserQuestionImpl(arguments_, context) {
|
|
2595
|
-
const parsed = askUserQuestionConfigSchema.safeParse(arguments_);
|
|
2596
|
-
if (!parsed.success) {
|
|
2597
|
-
return { error: `invalid_askUserQuestion_args: ${parsed.error.message}` };
|
|
2598
|
-
}
|
|
2599
|
-
const { question, inputType, options, allowFreeform, timeoutMs } = parsed.data;
|
|
2600
|
-
const questionId = crypto.randomUUID();
|
|
2601
|
-
const conversationId = context.agent?.id ?? context.activeToolConversationId;
|
|
2602
|
-
context.notifyAskQuestion?.({
|
|
2603
|
-
questionId,
|
|
2604
|
-
question,
|
|
2605
|
-
conversationId,
|
|
2606
|
-
inputType,
|
|
2607
|
-
options,
|
|
2608
|
-
allowFreeform
|
|
2609
|
-
});
|
|
2610
|
-
try {
|
|
2611
|
-
const answer = await waitForQuestionAnswer(questionId, timeoutMs);
|
|
2612
|
-
return { result: answer };
|
|
2613
|
-
} catch (error) {
|
|
2614
|
-
const message = error instanceof Error ? error.message : "askUserQuestion_failed";
|
|
2615
|
-
return { error: message };
|
|
2616
|
-
}
|
|
2617
|
-
}
|
|
2618
|
-
|
|
2619
|
-
// src/tools/builtins/mcpClient.ts
|
|
2620
|
-
var TOOL_ID = "mcpClient";
|
|
2621
|
-
var mcpClientConfigSchema = {
|
|
2622
|
-
type: "object",
|
|
2623
|
-
properties: {
|
|
2624
|
-
nodeId: { type: "string", description: "Target node ID" },
|
|
2625
|
-
serverName: { type: "string", description: "MCP server name on that node" },
|
|
2626
|
-
toolName: { type: "string", description: "Tool to invoke" },
|
|
2627
|
-
args: { type: "object", description: "Tool arguments" }
|
|
2628
|
-
},
|
|
2629
|
-
required: ["nodeId", "serverName", "toolName"]
|
|
2630
|
-
};
|
|
2631
|
-
var mcpClientImpl = async (arguments_, context) => {
|
|
2632
|
-
const nodeId = arguments_.nodeId;
|
|
2633
|
-
const serverName = arguments_.serverName;
|
|
2634
|
-
const toolName = arguments_.toolName;
|
|
2635
|
-
const toolArguments = arguments_.args ?? {};
|
|
2636
|
-
if (!nodeId || !serverName || !toolName) {
|
|
2637
|
-
return {
|
|
2638
|
-
error: "mcpClient requires nodeId, serverName, and toolName"
|
|
2639
|
-
};
|
|
2640
|
-
}
|
|
2641
|
-
if (!context.mcpCallRemote) {
|
|
2642
|
-
return {
|
|
2643
|
-
error: "MCP proxy not configured (no mcpCallRemote in context). Connect to nodes that expose MCP."
|
|
2644
|
-
};
|
|
2645
|
-
}
|
|
2646
|
-
try {
|
|
2647
|
-
const result = await context.mcpCallRemote(nodeId, serverName, toolName, toolArguments);
|
|
2648
|
-
return { result };
|
|
2649
|
-
} catch (error) {
|
|
2650
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2651
|
-
return { error: `MCP call failed: ${message}` };
|
|
2652
|
-
}
|
|
2653
|
-
};
|
|
2654
|
-
function getMcpClientToolId() {
|
|
2655
|
-
return TOOL_ID;
|
|
2656
|
-
}
|
|
2657
|
-
|
|
2658
|
-
// src/tools/builtins/mcpForward.ts
|
|
2659
|
-
var TOOL_ID2 = "mcpForward";
|
|
2660
|
-
var mcpForwardConfigSchema = {
|
|
2661
|
-
type: "object",
|
|
2662
|
-
properties: {
|
|
2663
|
-
action: {
|
|
2664
|
-
type: "string",
|
|
2665
|
-
enum: ["list", "listTools"],
|
|
2666
|
-
description: "Action: 'list' returns nodes with MCP servers, 'listTools' returns all available MCP tools across nodes"
|
|
2667
|
-
}
|
|
2668
|
-
}
|
|
2669
|
-
};
|
|
2670
|
-
var mcpForwardImpl = async (arguments_, context) => {
|
|
2671
|
-
const action = arguments_.action ?? "list";
|
|
2672
|
-
if (!context.getPeers) {
|
|
2673
|
-
return { error: "Peer list not configured (no getPeers)." };
|
|
2674
|
-
}
|
|
2675
|
-
if (!context.sendRpcToNode) {
|
|
2676
|
-
return { error: "Remote node RPC not configured (no sendRpcToNode)." };
|
|
2677
|
-
}
|
|
2678
|
-
const peers = await context.getPeers();
|
|
2679
|
-
const online = peers.filter((p) => p.reachability.state === "online");
|
|
2680
|
-
if (action === "list") {
|
|
2681
|
-
const result = [];
|
|
2682
|
-
for (const node of online) {
|
|
2683
|
-
try {
|
|
2684
|
-
const response = await context.sendRpcToNode(
|
|
2685
|
-
node.peerId,
|
|
2686
|
-
"memeloop.mcp.listServers",
|
|
2687
|
-
{}
|
|
2688
|
-
);
|
|
2689
|
-
const servers = Array.isArray(response?.servers) ? response.servers : [];
|
|
2690
|
-
if (servers.length > 0) {
|
|
2691
|
-
result.push({
|
|
2692
|
-
nodeId: node.peerId,
|
|
2693
|
-
name: node.displayName,
|
|
2694
|
-
mcpServers: servers
|
|
2695
|
-
});
|
|
2696
|
-
}
|
|
2697
|
-
} catch {
|
|
2698
|
-
}
|
|
2699
|
-
}
|
|
2700
|
-
return { nodes: result };
|
|
2701
|
-
}
|
|
2702
|
-
if (action === "listTools") {
|
|
2703
|
-
const allTools = [];
|
|
2704
|
-
for (const node of online) {
|
|
2705
|
-
try {
|
|
2706
|
-
const response = await context.sendRpcToNode(
|
|
2707
|
-
node.peerId,
|
|
2708
|
-
"memeloop.mcp.listTools",
|
|
2709
|
-
{}
|
|
2710
|
-
);
|
|
2711
|
-
const tools = Array.isArray(response?.tools) ? response.tools : [];
|
|
2712
|
-
for (const tool of tools) {
|
|
2713
|
-
allTools.push({
|
|
2714
|
-
nodeId: node.peerId,
|
|
2715
|
-
serverName: tool.serverName,
|
|
2716
|
-
name: tool.name,
|
|
2717
|
-
description: tool.description
|
|
2718
|
-
});
|
|
2719
|
-
}
|
|
2720
|
-
} catch {
|
|
2721
|
-
}
|
|
2722
|
-
}
|
|
2723
|
-
return { tools: allTools };
|
|
2724
|
-
}
|
|
2725
|
-
return { error: `Unknown action: ${action}` };
|
|
2726
|
-
};
|
|
2727
|
-
function getMcpForwardToolId() {
|
|
2728
|
-
return TOOL_ID2;
|
|
2729
|
-
}
|
|
2730
|
-
|
|
2731
|
-
// src/tools/builtins/remoteAgent.ts
|
|
2732
|
-
var TOOL_ID3 = "remoteAgent";
|
|
2733
|
-
var REMOTE_LOG_POLL_INTERVAL_MS = 500;
|
|
2734
|
-
var REMOTE_LOG_IDLE_POLLS = 2;
|
|
2735
|
-
function messageContentToText(content) {
|
|
2736
|
-
if (typeof content === "string") return content;
|
|
2737
|
-
if (content == null) return "";
|
|
2738
|
-
try {
|
|
2739
|
-
return JSON.stringify(content);
|
|
2740
|
-
} catch {
|
|
2741
|
-
return Object.prototype.toString.call(content);
|
|
2742
|
-
}
|
|
2743
|
-
}
|
|
2744
|
-
function getRemoteStreamChunkContent(chunk) {
|
|
2745
|
-
if (chunk != null && typeof chunk === "object" && "content" in chunk) {
|
|
2746
|
-
return Reflect.get(chunk, "content");
|
|
2747
|
-
}
|
|
2748
|
-
return void 0;
|
|
2749
|
-
}
|
|
2750
|
-
function summarizeRemoteMessages(messages) {
|
|
2751
|
-
const relevant = messages.filter((message) => message.role && message.role !== "user");
|
|
2752
|
-
if (relevant.length === 0) {
|
|
2753
|
-
return "(task dispatched; waiting for remote output)";
|
|
2754
|
-
}
|
|
2755
|
-
const joined = relevant.map((message) => {
|
|
2756
|
-
const role = typeof message.role === "string" ? message.role : "message";
|
|
2757
|
-
const content = messageContentToText(message.content).trim();
|
|
2758
|
-
return content.length > 0 ? `[${role}] ${content}` : "";
|
|
2759
|
-
}).filter((line) => line.length > 0).join("\n");
|
|
2760
|
-
return joined.trim() || "(task dispatched; remote messages had no readable content)";
|
|
2761
|
-
}
|
|
2762
|
-
async function collectRemoteConversationSummary(sendRpc, nodeId, conversationId, timeoutMs) {
|
|
2763
|
-
const startedAt = Date.now();
|
|
2764
|
-
const knownMessageIds = /* @__PURE__ */ new Set();
|
|
2765
|
-
const collectedMessages = [];
|
|
2766
|
-
let idlePolls = 0;
|
|
2767
|
-
while (Date.now() - startedAt < timeoutMs) {
|
|
2768
|
-
const response = await sendRpc(nodeId, "memeloop.chat.pullAgentRunLog", {
|
|
2769
|
-
conversationId,
|
|
2770
|
-
knownMessageIds: [...knownMessageIds]
|
|
2771
|
-
});
|
|
2772
|
-
const newMessages = Array.isArray(response?.messages) ? response.messages : [];
|
|
2773
|
-
if (newMessages.length > 0) {
|
|
2774
|
-
idlePolls = 0;
|
|
2775
|
-
for (const message of newMessages) {
|
|
2776
|
-
if (typeof message.messageId === "string" && message.messageId.length > 0) {
|
|
2777
|
-
knownMessageIds.add(message.messageId);
|
|
2778
|
-
}
|
|
2779
|
-
collectedMessages.push(message);
|
|
2780
|
-
}
|
|
2781
|
-
} else if (collectedMessages.some((message) => message.role && message.role !== "user")) {
|
|
2782
|
-
idlePolls += 1;
|
|
2783
|
-
if (idlePolls >= REMOTE_LOG_IDLE_POLLS) {
|
|
2784
|
-
break;
|
|
2785
|
-
}
|
|
2786
|
-
}
|
|
2787
|
-
await new Promise((resolve) => setTimeout(resolve, REMOTE_LOG_POLL_INTERVAL_MS));
|
|
2788
|
-
}
|
|
2789
|
-
return summarizeRemoteMessages(collectedMessages);
|
|
2790
|
-
}
|
|
2791
|
-
var remoteAgentConfigSchema = {
|
|
2792
|
-
type: "object",
|
|
2793
|
-
properties: {
|
|
2794
|
-
nodeId: { type: "string", description: "Target node ID to run the agent-run on" },
|
|
2795
|
-
definitionId: { type: "string", description: "Agent definition ID on that node" },
|
|
2796
|
-
message: { type: "string", description: "Task message for the remote agent" }
|
|
2797
|
-
},
|
|
2798
|
-
required: ["nodeId", "definitionId", "message"]
|
|
2799
|
-
};
|
|
2800
|
-
var remoteAgentListImpl = async (_arguments, context) => {
|
|
2801
|
-
const getPeers = context.getPeers ? async () => context.getPeers?.() : void 0;
|
|
2802
|
-
const sendRpc = context.sendRpcToNode ? async (nodeId, method, parameters) => context.sendRpcToNode?.(nodeId, method, parameters) : void 0;
|
|
2803
|
-
if (!getPeers) {
|
|
2804
|
-
return { nodes: [], error: "Peer list not configured (no getPeers)." };
|
|
2805
|
-
}
|
|
2806
|
-
const peers = await getPeers() ?? [];
|
|
2807
|
-
const online = peers.filter((p) => p.reachability?.state === "online");
|
|
2808
|
-
const result = [];
|
|
2809
|
-
for (const node of online) {
|
|
2810
|
-
const entry = {
|
|
2811
|
-
nodeId: node.peerId,
|
|
2812
|
-
name: node.displayName
|
|
2813
|
-
};
|
|
2814
|
-
if (sendRpc) {
|
|
2815
|
-
try {
|
|
2816
|
-
const response = await sendRpc(
|
|
2817
|
-
node.peerId,
|
|
2818
|
-
"memeloop.agent.getDefinitions",
|
|
2819
|
-
{}
|
|
2820
|
-
);
|
|
2821
|
-
entry.definitions = Array.isArray(response?.definitions) ? response.definitions : [];
|
|
2822
|
-
} catch {
|
|
2823
|
-
entry.definitions = [];
|
|
2824
|
-
}
|
|
2825
|
-
}
|
|
2826
|
-
result.push(entry);
|
|
2827
|
-
}
|
|
2828
|
-
return { nodes: result };
|
|
2829
|
-
};
|
|
2830
|
-
var remoteAgentImpl = async (arguments_, context) => {
|
|
2831
|
-
const nodeId = arguments_.nodeId;
|
|
2832
|
-
const definitionId = arguments_.definitionId;
|
|
2833
|
-
const message = arguments_.message;
|
|
2834
|
-
if (!nodeId || !definitionId || typeof message !== "string") {
|
|
2835
|
-
return remoteAgentListImpl(arguments_, context);
|
|
2836
|
-
}
|
|
2837
|
-
const sendRpc = context.sendRpcToNode ? async (nodeId2, method, parameters) => context.sendRpcToNode?.(nodeId2, method, parameters) : void 0;
|
|
2838
|
-
if (!sendRpc) {
|
|
2839
|
-
return {
|
|
2840
|
-
error: "Remote node RPC not configured (no sendRpcToNode). Connect to peer nodes first."
|
|
2841
|
-
};
|
|
2842
|
-
}
|
|
2843
|
-
try {
|
|
2844
|
-
const createResult = await sendRpc(nodeId, "memeloop.agent.create", {
|
|
2845
|
-
definitionId
|
|
2846
|
-
});
|
|
2847
|
-
const conversationId = createResult?.conversationId;
|
|
2848
|
-
if (!conversationId) {
|
|
2849
|
-
return { error: "Remote agent.create did not return conversationId", raw: createResult };
|
|
2850
|
-
}
|
|
2851
|
-
await sendRpc(nodeId, "memeloop.agent.send", {
|
|
2852
|
-
conversationId,
|
|
2853
|
-
message
|
|
2854
|
-
});
|
|
2855
|
-
const subscribeStream = context.subscribeRemoteStream;
|
|
2856
|
-
const streamWaitMs = context.remoteAgentStreamTimeoutMs ?? 3e4;
|
|
2857
|
-
const chunks = [];
|
|
2858
|
-
if (subscribeStream) {
|
|
2859
|
-
await new Promise((resolve) => {
|
|
2860
|
-
const unsub = subscribeStream(nodeId, conversationId, (chunk) => {
|
|
2861
|
-
if (typeof chunk === "string") chunks.push(chunk);
|
|
2862
|
-
else {
|
|
2863
|
-
const chunkContent = getRemoteStreamChunkContent(chunk);
|
|
2864
|
-
if (chunkContent !== void 0) {
|
|
2865
|
-
chunks.push(messageContentToText(chunkContent));
|
|
2866
|
-
}
|
|
2867
|
-
}
|
|
2868
|
-
});
|
|
2869
|
-
setTimeout(() => {
|
|
2870
|
-
unsub();
|
|
2871
|
-
resolve();
|
|
2872
|
-
}, streamWaitMs);
|
|
2873
|
-
});
|
|
2874
|
-
}
|
|
2875
|
-
const fullSummary = chunks.length > 0 ? chunks.join("").trim() : await collectRemoteConversationSummary(sendRpc, nodeId, conversationId, streamWaitMs);
|
|
2876
|
-
const shortSummary = truncateToolSummary(fullSummary);
|
|
2877
|
-
return {
|
|
2878
|
-
summary: fullSummary,
|
|
2879
|
-
remoteNodeId: nodeId,
|
|
2880
|
-
remoteConversationId: conversationId,
|
|
2881
|
-
definitionId,
|
|
2882
|
-
[MEMELOOP_STRUCTURED_TOOL_KEY]: {
|
|
2883
|
-
summary: shortSummary,
|
|
2884
|
-
detailRef: {
|
|
2885
|
-
type: "agent-run",
|
|
2886
|
-
conversationId,
|
|
2887
|
-
nodeId
|
|
2888
|
-
}
|
|
2889
|
-
}
|
|
2890
|
-
};
|
|
2891
|
-
} catch (error) {
|
|
2892
|
-
const message2 = error instanceof Error ? error.message : messageContentToText(error);
|
|
2893
|
-
return { error: `remoteAgent failed: ${message2}` };
|
|
2894
|
-
}
|
|
2895
|
-
};
|
|
2896
|
-
function getRemoteAgentToolId() {
|
|
2897
|
-
return TOOL_ID3;
|
|
2898
|
-
}
|
|
2899
|
-
|
|
2900
|
-
// src/tools/builtins/spawnAgent.ts
|
|
2901
|
-
var TOOL_ID4 = "spawnAgent";
|
|
2902
|
-
var spawnAgentConfigSchema = {
|
|
2903
|
-
type: "object",
|
|
2904
|
-
properties: {
|
|
2905
|
-
definitionId: { type: "string", description: "Agent definition ID to use for the agent-run" },
|
|
2906
|
-
message: { type: "string", description: "User message / task for the agent-run" }
|
|
2907
|
-
},
|
|
2908
|
-
required: ["definitionId", "message"]
|
|
2909
|
-
};
|
|
2910
|
-
var spawnAgentImpl = async (arguments_, context) => {
|
|
2911
|
-
const definitionId = arguments_.definitionId;
|
|
2912
|
-
const message = arguments_.message;
|
|
2913
|
-
if (!definitionId || typeof message !== "string") {
|
|
2914
|
-
return { error: "spawnAgent requires definitionId and message" };
|
|
2915
|
-
}
|
|
2916
|
-
if (!context.runLocalAgent) {
|
|
2917
|
-
return {
|
|
2918
|
-
error: "Local agent runner not configured (no runLocalAgent in context)."
|
|
2919
|
-
};
|
|
2920
|
-
}
|
|
2921
|
-
const conversationId = `spawn:${definitionId}:${Date.now().toString(36)}`;
|
|
2922
|
-
const chunks = [];
|
|
2923
|
-
try {
|
|
2924
|
-
for await (const step of context.runLocalAgent({ conversationId, message })) {
|
|
2925
|
-
if (step.type === "message" && typeof step.data === "string") {
|
|
2926
|
-
chunks.push(step.data);
|
|
2927
|
-
}
|
|
2928
|
-
if (step.type === "message" && step.data != null && typeof step.data === "object" && "content" in step.data) {
|
|
2929
|
-
const c = step.data.content;
|
|
2930
|
-
if (typeof c === "string") chunks.push(c);
|
|
2931
|
-
}
|
|
2932
|
-
}
|
|
2933
|
-
const fullSummary = chunks.join("").trim() || "(no text output)";
|
|
2934
|
-
const shortSummary = truncateToolSummary(fullSummary);
|
|
2935
|
-
const nodeId = context.localNodeId?.trim() || "local";
|
|
2936
|
-
return {
|
|
2937
|
-
summary: fullSummary,
|
|
2938
|
-
conversationId,
|
|
2939
|
-
definitionId,
|
|
2940
|
-
[MEMELOOP_STRUCTURED_TOOL_KEY]: {
|
|
2941
|
-
summary: shortSummary,
|
|
2942
|
-
detailRef: {
|
|
2943
|
-
type: "agent-run",
|
|
2944
|
-
conversationId,
|
|
2945
|
-
nodeId
|
|
2946
|
-
}
|
|
2947
|
-
}
|
|
2948
|
-
};
|
|
2949
|
-
} catch (error) {
|
|
2950
|
-
const message2 = error instanceof Error ? error.message : String(error);
|
|
2951
|
-
return { error: `spawnAgent failed: ${message2}`, conversationId };
|
|
2952
|
-
}
|
|
2953
|
-
};
|
|
2954
|
-
function getSpawnAgentToolId() {
|
|
2955
|
-
return TOOL_ID4;
|
|
2956
|
-
}
|
|
2957
|
-
|
|
2958
|
-
// src/agent/agentProfiles.ts
|
|
2959
|
-
function makeBuiltinDefinition(id, name, type, prompt, permissions, model) {
|
|
2960
|
-
const systemPrompt = prompt;
|
|
2961
|
-
const tools = [];
|
|
2962
|
-
return {
|
|
2963
|
-
id,
|
|
2964
|
-
name,
|
|
2965
|
-
type,
|
|
2966
|
-
prompt,
|
|
2967
|
-
permissions,
|
|
2968
|
-
model,
|
|
2969
|
-
protocolDef: {
|
|
2970
|
-
id,
|
|
2971
|
-
name,
|
|
2972
|
-
description: prompt.slice(0, 200),
|
|
2973
|
-
systemPrompt,
|
|
2974
|
-
tools,
|
|
2975
|
-
version: "1.0.0",
|
|
2976
|
-
modelConfig: model ? {
|
|
2977
|
-
provider: model.split("/")[0] ?? "memeloop",
|
|
2978
|
-
model: model.split("/").slice(1).join("/") || model
|
|
2979
|
-
} : void 0
|
|
2980
|
-
}
|
|
2981
|
-
};
|
|
2982
|
-
}
|
|
2983
|
-
var buildAgent = makeBuiltinDefinition(
|
|
2984
|
-
"memeloop:build",
|
|
2985
|
-
"Build Agent",
|
|
2986
|
-
"build",
|
|
2987
|
-
"You are a build agent capable of executing tasks, writing and editing files, running commands, and making changes. Use tools to accomplish the user's goals efficiently.",
|
|
2988
|
-
{ default: "allow", rules: [] }
|
|
2989
|
-
);
|
|
2990
|
-
var planAgent = makeBuiltinDefinition(
|
|
2991
|
-
"memeloop:plan",
|
|
2992
|
-
"Plan Agent",
|
|
2993
|
-
"plan",
|
|
2994
|
-
"You are a planning agent. Your role is to analyze requirements, decompose tasks, and create structured plans. You CANNOT write files or execute terminal commands. Only use read and search tools.",
|
|
2995
|
-
{
|
|
2996
|
-
default: "deny",
|
|
2997
|
-
rules: [
|
|
2998
|
-
{ pattern: "file.read", action: "allow" },
|
|
2999
|
-
{ pattern: "file.search", action: "allow" },
|
|
3000
|
-
{ pattern: "file.list", action: "allow" },
|
|
3001
|
-
{ pattern: "grep.search", action: "allow" },
|
|
3002
|
-
{ pattern: "glob.*", action: "allow" }
|
|
3003
|
-
]
|
|
3004
|
-
}
|
|
3005
|
-
);
|
|
3006
|
-
var exploreAgent = makeBuiltinDefinition(
|
|
3007
|
-
"memeloop:explore",
|
|
3008
|
-
"Explore Agent",
|
|
3009
|
-
"explore",
|
|
3010
|
-
"You are an exploration agent specialized in fast codebase search and discovery. You have read and search tools. Do NOT modify files or run terminal commands. Focus on finding relevant code, files, and patterns.",
|
|
3011
|
-
{
|
|
3012
|
-
default: "deny",
|
|
3013
|
-
rules: [
|
|
3014
|
-
{ pattern: "file.read", action: "allow" },
|
|
3015
|
-
{ pattern: "file.search", action: "allow" },
|
|
3016
|
-
{ pattern: "file.list", action: "allow" },
|
|
3017
|
-
{ pattern: "grep.search", action: "allow" },
|
|
3018
|
-
{ pattern: "glob.*", action: "allow" },
|
|
3019
|
-
{ pattern: "lsp.*", action: "allow" }
|
|
3020
|
-
]
|
|
3021
|
-
}
|
|
3022
|
-
);
|
|
3023
|
-
var oracleAgent = makeBuiltinDefinition(
|
|
3024
|
-
"memeloop:oracle",
|
|
3025
|
-
"Oracle Agent",
|
|
3026
|
-
"oracle",
|
|
3027
|
-
"You are an oracle agent specialized in architecture analysis, code review, and constraint verification. You have read access only. Review code, identify issues, and provide expert architectural guidance.",
|
|
3028
|
-
{
|
|
3029
|
-
default: "deny",
|
|
3030
|
-
rules: [
|
|
3031
|
-
{ pattern: "file.read", action: "allow" },
|
|
3032
|
-
{ pattern: "file.search", action: "allow" },
|
|
3033
|
-
{ pattern: "file.list", action: "allow" },
|
|
3034
|
-
{ pattern: "grep.search", action: "allow" },
|
|
3035
|
-
{ pattern: "glob.*", action: "allow" },
|
|
3036
|
-
{ pattern: "lsp.*", action: "allow" }
|
|
3037
|
-
]
|
|
3038
|
-
}
|
|
3039
|
-
);
|
|
3040
|
-
var librarianAgent = makeBuiltinDefinition(
|
|
3041
|
-
"memeloop:librarian",
|
|
3042
|
-
"Librarian Agent",
|
|
3043
|
-
"librarian",
|
|
3044
|
-
"You are a librarian agent specialized in external documentation lookup, web search, and context gathering. Use web search tools to find relevant documentation, APIs, and examples. Do NOT modify files or run terminal commands.",
|
|
3045
|
-
{
|
|
3046
|
-
default: "deny",
|
|
3047
|
-
rules: [
|
|
3048
|
-
{ pattern: "file.read", action: "allow" },
|
|
3049
|
-
{ pattern: "file.search", action: "allow" },
|
|
3050
|
-
{ pattern: "file.list", action: "allow" },
|
|
3051
|
-
{ pattern: "web.*", action: "allow" },
|
|
3052
|
-
{ pattern: "grep.search", action: "allow" },
|
|
3053
|
-
{ pattern: "glob.*", action: "allow" }
|
|
3054
|
-
]
|
|
3055
|
-
}
|
|
3056
|
-
);
|
|
3057
|
-
var BUILTIN_AGENT_PROFILES = [
|
|
3058
|
-
buildAgent,
|
|
3059
|
-
planAgent,
|
|
3060
|
-
exploreAgent,
|
|
3061
|
-
oracleAgent,
|
|
3062
|
-
librarianAgent
|
|
3063
|
-
];
|
|
3064
|
-
|
|
3065
|
-
// src/agent/agentProfileRegistry.ts
|
|
3066
|
-
var AgentProfileRegistry = class {
|
|
3067
|
-
profiles = /* @__PURE__ */ new Map();
|
|
3068
|
-
constructor() {
|
|
3069
|
-
this.seedDefaults();
|
|
3070
|
-
}
|
|
3071
|
-
/** Seed the registry with built-in agent profiles. */
|
|
3072
|
-
seedDefaults() {
|
|
3073
|
-
for (const profile of BUILTIN_AGENT_PROFILES) {
|
|
3074
|
-
this.profiles.set(profile.id, profile);
|
|
3075
|
-
}
|
|
3076
|
-
}
|
|
3077
|
-
/**
|
|
3078
|
-
* Register a new agent profile or override an existing one.
|
|
3079
|
-
* Throws if the profile is invalid.
|
|
3080
|
-
*/
|
|
3081
|
-
registerAgentProfile(profile) {
|
|
3082
|
-
if (!profile.id || typeof profile.id !== "string" || profile.id.trim().length === 0) {
|
|
3083
|
-
throw new Error("Agent profile must have a non-empty id");
|
|
3084
|
-
}
|
|
3085
|
-
if (!profile.name || typeof profile.name !== "string") {
|
|
3086
|
-
throw new Error("Agent profile must have a name");
|
|
3087
|
-
}
|
|
3088
|
-
if (!profile.type || typeof profile.type !== "string") {
|
|
3089
|
-
throw new Error("Agent profile must have a type");
|
|
3090
|
-
}
|
|
3091
|
-
if (!profile.prompt || typeof profile.prompt !== "string") {
|
|
3092
|
-
throw new Error("Agent profile must have a prompt");
|
|
3093
|
-
}
|
|
3094
|
-
if (!profile.permissions || typeof profile.permissions.default !== "string") {
|
|
3095
|
-
throw new Error("Agent profile must have valid permissions");
|
|
3096
|
-
}
|
|
3097
|
-
this.profiles.set(profile.id, profile);
|
|
3098
|
-
}
|
|
3099
|
-
/**
|
|
3100
|
-
* Get an agent profile by ID.
|
|
3101
|
-
* Returns undefined if not found.
|
|
3102
|
-
*/
|
|
3103
|
-
getAgentProfile(id) {
|
|
3104
|
-
return this.profiles.get(id);
|
|
3105
|
-
}
|
|
3106
|
-
/**
|
|
3107
|
-
* List all registered agent profiles.
|
|
3108
|
-
*/
|
|
3109
|
-
listAgentProfiles() {
|
|
3110
|
-
return Array.from(this.profiles.values());
|
|
3111
|
-
}
|
|
3112
|
-
/**
|
|
3113
|
-
* List agent profiles filtered by type.
|
|
3114
|
-
*/
|
|
3115
|
-
listAgentProfilesByType(type) {
|
|
3116
|
-
return this.listAgentProfiles().filter((profile) => profile.type === type);
|
|
3117
|
-
}
|
|
3118
|
-
/**
|
|
3119
|
-
* Remove an agent profile by ID.
|
|
3120
|
-
*/
|
|
3121
|
-
unregisterAgentProfile(id) {
|
|
3122
|
-
return this.profiles.delete(id);
|
|
3123
|
-
}
|
|
3124
|
-
/**
|
|
3125
|
-
* Reset the registry to only built-in defaults.
|
|
3126
|
-
*/
|
|
3127
|
-
reset() {
|
|
3128
|
-
this.profiles.clear();
|
|
3129
|
-
this.seedDefaults();
|
|
3130
|
-
}
|
|
3131
|
-
};
|
|
3132
|
-
var defaultRegistry2 = null;
|
|
3133
|
-
function getAgentProfileRegistry() {
|
|
3134
|
-
if (!defaultRegistry2) {
|
|
3135
|
-
defaultRegistry2 = new AgentProfileRegistry();
|
|
3136
|
-
}
|
|
3137
|
-
return defaultRegistry2;
|
|
3138
|
-
}
|
|
3139
|
-
function resetAgentProfileRegistry() {
|
|
3140
|
-
if (defaultRegistry2) {
|
|
3141
|
-
defaultRegistry2.reset();
|
|
3142
|
-
}
|
|
3143
|
-
defaultRegistry2 = null;
|
|
3144
|
-
}
|
|
3145
|
-
|
|
3146
|
-
// src/tools/builtins/task.ts
|
|
3147
|
-
var TOOL_ID5 = "task";
|
|
3148
|
-
var taskToolConfigSchema = {
|
|
3149
|
-
type: "object",
|
|
3150
|
-
properties: {
|
|
3151
|
-
agent: {
|
|
3152
|
-
type: "string",
|
|
3153
|
-
description: "Agent ID to delegate to (e.g. 'memeloop:build', 'memeloop:explore')"
|
|
3154
|
-
},
|
|
3155
|
-
prompt: {
|
|
3156
|
-
type: "string",
|
|
3157
|
-
description: "Task prompt / instructions for the delegated agent"
|
|
3158
|
-
},
|
|
3159
|
-
background: {
|
|
3160
|
-
type: "boolean",
|
|
3161
|
-
description: "If true, runs asynchronously and returns a task ID immediately"
|
|
3162
|
-
}
|
|
3163
|
-
},
|
|
3164
|
-
required: ["agent", "prompt"]
|
|
3165
|
-
};
|
|
3166
|
-
function isTooDeeplyNested(context) {
|
|
3167
|
-
const cid = context.activeToolConversationId;
|
|
3168
|
-
if (!cid) return false;
|
|
3169
|
-
const depth = cid.split(":").length - 1;
|
|
3170
|
-
return depth > 2;
|
|
3171
|
-
}
|
|
3172
|
-
function applyAgentPermissions(context, agentId, defaultAction, rules) {
|
|
3173
|
-
const tp = context.agentToolLoop ?? (context.agentToolLoop = {});
|
|
3174
|
-
const tperm = tp.toolPermissions ?? (tp.toolPermissions = {});
|
|
3175
|
-
const perAgent = tperm.perAgent ?? (tperm.perAgent = {});
|
|
3176
|
-
perAgent[agentId] = { default: defaultAction, rules };
|
|
3177
|
-
}
|
|
3178
|
-
var taskToolImpl = async (arguments_, context) => {
|
|
3179
|
-
const agentId = arguments_.agent;
|
|
3180
|
-
const prompt = arguments_.prompt;
|
|
3181
|
-
const background = arguments_.background;
|
|
3182
|
-
if (!agentId || typeof prompt !== "string" || prompt.trim().length === 0) {
|
|
3183
|
-
return { error: "task requires 'agent' (string) and 'prompt' (non-empty string)" };
|
|
3184
|
-
}
|
|
3185
|
-
if (isTooDeeplyNested(context)) {
|
|
3186
|
-
return {
|
|
3187
|
-
error: "Maximum agent nesting depth exceeded. Cannot delegate further."
|
|
3188
|
-
};
|
|
3189
|
-
}
|
|
3190
|
-
const registry = getAgentProfileRegistry();
|
|
3191
|
-
const agentProfile = registry.getAgentProfile(agentId);
|
|
3192
|
-
if (!agentProfile) {
|
|
3193
|
-
const available = registry.listAgentProfiles().map((a) => a.id).join(", ");
|
|
3194
|
-
return {
|
|
3195
|
-
error: `Agent "${agentId}" not found in registry. Available: [${available}]`
|
|
3196
|
-
};
|
|
3197
|
-
}
|
|
3198
|
-
applyAgentPermissions(
|
|
3199
|
-
context,
|
|
3200
|
-
agentProfile.id,
|
|
3201
|
-
agentProfile.permissions.default,
|
|
3202
|
-
agentProfile.permissions.rules
|
|
3203
|
-
);
|
|
3204
|
-
const timestamp = Date.now().toString(36);
|
|
3205
|
-
const conversationId = `${agentProfile.id}:${timestamp}`;
|
|
3206
|
-
const runLocal = context.runLocalAgent?.bind(context);
|
|
3207
|
-
if (!runLocal) {
|
|
3208
|
-
return {
|
|
3209
|
-
error: "Local agent runner not configured (no runLocalAgent in context)."
|
|
3210
|
-
};
|
|
3211
|
-
}
|
|
3212
|
-
async function collectOutput(gen) {
|
|
3213
|
-
const chunks = [];
|
|
3214
|
-
for await (const step of gen) {
|
|
3215
|
-
if (step.type === "message") {
|
|
3216
|
-
if (typeof step.data === "string") {
|
|
3217
|
-
chunks.push(step.data);
|
|
3218
|
-
} else if (step.data != null && typeof step.data === "object" && "content" in step.data) {
|
|
3219
|
-
const c = step.data.content;
|
|
3220
|
-
if (typeof c === "string") chunks.push(c);
|
|
3221
|
-
}
|
|
3222
|
-
}
|
|
3223
|
-
}
|
|
3224
|
-
return { text: chunks.join("").trim() || "(no text output)", conversationId };
|
|
3225
|
-
}
|
|
3226
|
-
if (background) {
|
|
3227
|
-
const gen = runLocal({ conversationId, message: prompt });
|
|
3228
|
-
void collectOutput(gen).catch(() => {
|
|
3229
|
-
});
|
|
3230
|
-
const nodeId = context.localNodeId?.trim() || "local";
|
|
3231
|
-
return {
|
|
3232
|
-
summary: `Background task "${agentId}" launched. Task ID: ${conversationId}`,
|
|
3233
|
-
conversationId,
|
|
3234
|
-
agentId,
|
|
3235
|
-
taskId: conversationId,
|
|
3236
|
-
background: true,
|
|
3237
|
-
[MEMELOOP_STRUCTURED_TOOL_KEY]: {
|
|
3238
|
-
summary: `[bg-task] ${agentId}: ${conversationId}`,
|
|
3239
|
-
detailRef: {
|
|
3240
|
-
type: "agent-run",
|
|
3241
|
-
conversationId,
|
|
3242
|
-
nodeId
|
|
3243
|
-
}
|
|
3244
|
-
}
|
|
3245
|
-
};
|
|
3246
|
-
}
|
|
3247
|
-
try {
|
|
3248
|
-
const gen = runLocal({ conversationId, message: prompt });
|
|
3249
|
-
const { text, conversationId: cid } = await collectOutput(gen);
|
|
3250
|
-
const shortSummary = truncateToolSummary(text);
|
|
3251
|
-
const nodeId = context.localNodeId?.trim() || "local";
|
|
3252
|
-
return {
|
|
3253
|
-
result: text,
|
|
3254
|
-
conversationId: cid,
|
|
3255
|
-
agentId,
|
|
3256
|
-
[MEMELOOP_STRUCTURED_TOOL_KEY]: {
|
|
3257
|
-
summary: shortSummary,
|
|
3258
|
-
detailRef: {
|
|
3259
|
-
type: "agent-run",
|
|
3260
|
-
conversationId: cid,
|
|
3261
|
-
nodeId
|
|
3262
|
-
}
|
|
3263
|
-
}
|
|
3264
|
-
};
|
|
3265
|
-
} catch (error) {
|
|
3266
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
3267
|
-
return {
|
|
3268
|
-
error: `Task execution failed: ${message}`,
|
|
3269
|
-
conversationId,
|
|
3270
|
-
agentId
|
|
3271
|
-
};
|
|
3272
|
-
}
|
|
3273
|
-
};
|
|
3274
|
-
function getTaskToolId() {
|
|
3275
|
-
return TOOL_ID5;
|
|
3276
|
-
}
|
|
3277
|
-
|
|
3278
|
-
// src/tools/builtins/todoWrite.ts
|
|
3279
|
-
import { z as z3 } from "zod";
|
|
3280
|
-
var todoWriteConfigSchema = z3.object({
|
|
3281
|
-
action: z3.enum(["create", "update", "complete", "list", "remove"]).describe("Action: create, update, complete, list, or remove"),
|
|
3282
|
-
id: z3.string().min(1).optional().describe("Todo item ID (required for update/complete/remove)"),
|
|
3283
|
-
content: z3.string().min(1).optional().describe("Todo item content (required for create/update)"),
|
|
3284
|
-
status: z3.enum(["pending", "in_progress", "completed", "cancelled"]).optional().describe("Status (defaults to 'pending' for create)"),
|
|
3285
|
-
priority: z3.enum(["high", "medium", "low"]).optional().default("medium").describe("Priority level")
|
|
3286
|
-
});
|
|
3287
|
-
var TODO_WRITE_TOOL_ID = "todoWrite";
|
|
3288
|
-
var conversationTodos = /* @__PURE__ */ new Map();
|
|
3289
|
-
function __clearTodoStore() {
|
|
3290
|
-
conversationTodos.clear();
|
|
3291
|
-
}
|
|
3292
|
-
function getConversationTodos(conversationId) {
|
|
3293
|
-
let todos = conversationTodos.get(conversationId);
|
|
3294
|
-
if (!todos) {
|
|
3295
|
-
todos = /* @__PURE__ */ new Map();
|
|
3296
|
-
conversationTodos.set(conversationId, todos);
|
|
3297
|
-
}
|
|
3298
|
-
return todos;
|
|
3299
|
-
}
|
|
3300
|
-
function formatTodoList(todos) {
|
|
3301
|
-
if (todos.size === 0) {
|
|
3302
|
-
return "(No todos)";
|
|
3303
|
-
}
|
|
3304
|
-
const statusOrder = {
|
|
3305
|
-
in_progress: 0,
|
|
3306
|
-
pending: 1,
|
|
3307
|
-
completed: 2,
|
|
3308
|
-
cancelled: 3
|
|
3309
|
-
};
|
|
3310
|
-
const sorted = [...todos.values()].sort(
|
|
3311
|
-
(a, b) => (statusOrder[a.status] ?? 99) - (statusOrder[b.status] ?? 99) || b.updatedAt - a.updatedAt
|
|
3312
|
-
);
|
|
3313
|
-
const statusIcons = {
|
|
3314
|
-
pending: "\u25CB",
|
|
3315
|
-
in_progress: "\u25C9",
|
|
3316
|
-
completed: "\u2713",
|
|
3317
|
-
cancelled: "\u2717"
|
|
3318
|
-
};
|
|
3319
|
-
return sorted.map(
|
|
3320
|
-
(t) => `${statusIcons[t.status] ?? "?"} [${t.priority}] ${t.content} (id: ${t.id})`
|
|
3321
|
-
).join("\n");
|
|
3322
|
-
}
|
|
3323
|
-
function getConversationId(context) {
|
|
3324
|
-
return context.agent?.id ?? context.activeToolConversationId ?? "default";
|
|
3325
|
-
}
|
|
3326
|
-
async function todoWriteImpl(arguments_, context) {
|
|
3327
|
-
const parsed = todoWriteConfigSchema.safeParse(arguments_);
|
|
3328
|
-
if (!parsed.success) {
|
|
3329
|
-
return { error: `invalid_todoWrite_args: ${parsed.error.message}` };
|
|
3330
|
-
}
|
|
3331
|
-
const { action, id, content, status, priority } = parsed.data;
|
|
3332
|
-
const conversationId = getConversationId(context);
|
|
3333
|
-
const todos = getConversationTodos(conversationId);
|
|
3334
|
-
const now = Date.now();
|
|
3335
|
-
switch (action) {
|
|
3336
|
-
case "create": {
|
|
3337
|
-
if (!content) {
|
|
3338
|
-
return { error: "content is required for create action" };
|
|
3339
|
-
}
|
|
3340
|
-
const newId = id ?? crypto.randomUUID();
|
|
3341
|
-
if (todos.has(newId)) {
|
|
3342
|
-
return { error: `Todo with id '${newId}' already exists. Use update action.` };
|
|
3343
|
-
}
|
|
3344
|
-
const item = {
|
|
3345
|
-
id: newId,
|
|
3346
|
-
content,
|
|
3347
|
-
status: status ?? "pending",
|
|
3348
|
-
priority,
|
|
3349
|
-
createdAt: now,
|
|
3350
|
-
updatedAt: now
|
|
3351
|
-
};
|
|
3352
|
-
todos.set(newId, item);
|
|
3353
|
-
return {
|
|
3354
|
-
result: `Todo created: [${item.priority}] ${item.content} (id: ${item.id})
|
|
3355
|
-
|
|
3356
|
-
${formatTodoList(todos)}`
|
|
3357
|
-
};
|
|
3358
|
-
}
|
|
3359
|
-
case "update": {
|
|
3360
|
-
if (!id) {
|
|
3361
|
-
return { error: "id is required for update action" };
|
|
3362
|
-
}
|
|
3363
|
-
const existing = todos.get(id);
|
|
3364
|
-
if (!existing) {
|
|
3365
|
-
return { error: `Todo with id '${id}' not found` };
|
|
3366
|
-
}
|
|
3367
|
-
if (content !== void 0) existing.content = content;
|
|
3368
|
-
if (status !== void 0) existing.status = status;
|
|
3369
|
-
existing.priority = priority ?? existing.priority;
|
|
3370
|
-
existing.updatedAt = now;
|
|
3371
|
-
return {
|
|
3372
|
-
result: `Todo updated: [${existing.priority}] ${existing.content} (id: ${existing.id}, status: ${existing.status})
|
|
3373
|
-
|
|
3374
|
-
${formatTodoList(todos)}`
|
|
3375
|
-
};
|
|
3376
|
-
}
|
|
3377
|
-
case "complete": {
|
|
3378
|
-
if (!id) {
|
|
3379
|
-
return { error: "id is required for complete action" };
|
|
3380
|
-
}
|
|
3381
|
-
const existing = todos.get(id);
|
|
3382
|
-
if (!existing) {
|
|
3383
|
-
return { error: `Todo with id '${id}' not found` };
|
|
3384
|
-
}
|
|
3385
|
-
existing.status = "completed";
|
|
3386
|
-
existing.updatedAt = now;
|
|
3387
|
-
return {
|
|
3388
|
-
result: `Todo completed: [${existing.priority}] ${existing.content} (id: ${existing.id})
|
|
3389
|
-
|
|
3390
|
-
${formatTodoList(todos)}`
|
|
3391
|
-
};
|
|
3392
|
-
}
|
|
3393
|
-
case "remove": {
|
|
3394
|
-
if (!id) {
|
|
3395
|
-
let removed2 = 0;
|
|
3396
|
-
for (const [key, item] of todos) {
|
|
3397
|
-
if (item.status === "completed" || item.status === "cancelled") {
|
|
3398
|
-
todos.delete(key);
|
|
3399
|
-
removed2++;
|
|
3400
|
-
}
|
|
3401
|
-
}
|
|
3402
|
-
return {
|
|
3403
|
-
result: `Removed ${removed2} completed/cancelled todo(s)
|
|
3404
|
-
|
|
3405
|
-
${formatTodoList(todos)}`
|
|
3406
|
-
};
|
|
3407
|
-
}
|
|
3408
|
-
const removed = todos.delete(id);
|
|
3409
|
-
return {
|
|
3410
|
-
result: removed ? `Todo removed: ${id}
|
|
3411
|
-
|
|
3412
|
-
${formatTodoList(todos)}` : `Todo with id '${id}' not found
|
|
3413
|
-
|
|
3414
|
-
${formatTodoList(todos)}`
|
|
3415
|
-
};
|
|
3416
|
-
}
|
|
3417
|
-
case "list":
|
|
3418
|
-
default: {
|
|
3419
|
-
return {
|
|
3420
|
-
result: `Todo list (${todos.size} items):
|
|
3421
|
-
|
|
3422
|
-
${formatTodoList(todos)}`
|
|
3423
|
-
};
|
|
3424
|
-
}
|
|
3425
|
-
}
|
|
3426
|
-
}
|
|
3427
|
-
|
|
3428
|
-
// src/loopAPI/plugins/builtinToolsPlugin.ts
|
|
3429
|
-
var PLUGIN_MCP_CLIENT = "builtin:mcp-client";
|
|
3430
|
-
var PLUGIN_MCP_FORWARD = "builtin:mcp-forward";
|
|
3431
|
-
var PLUGIN_SPAWN_AGENT = "builtin:spawn-agent";
|
|
3432
|
-
var PLUGIN_REMOTE_AGENT = "builtin:remote-agent";
|
|
3433
|
-
var PLUGIN_ASK_QUESTION = "builtin:ask-question";
|
|
3434
|
-
var PLUGIN_TODO_WRITE = "builtin:todo-write";
|
|
3435
|
-
var PLUGIN_ASK_USER_QUESTION = "builtin:ask-user-question";
|
|
3436
|
-
var PLUGIN_TASK = "builtin:task";
|
|
3437
|
-
var BUILTIN_TOOL_PLUGIN_IDS = [
|
|
3438
|
-
PLUGIN_MCP_CLIENT,
|
|
3439
|
-
PLUGIN_MCP_FORWARD,
|
|
3440
|
-
PLUGIN_SPAWN_AGENT,
|
|
3441
|
-
PLUGIN_REMOTE_AGENT,
|
|
3442
|
-
PLUGIN_ASK_QUESTION,
|
|
3443
|
-
PLUGIN_TODO_WRITE,
|
|
3444
|
-
PLUGIN_ASK_USER_QUESTION,
|
|
3445
|
-
PLUGIN_TASK
|
|
3446
|
-
];
|
|
3447
|
-
function getToolRegistry(context) {
|
|
3448
|
-
return context.toolRegistry;
|
|
3449
|
-
}
|
|
3450
|
-
function createBuiltinToolPlugin(options) {
|
|
3451
|
-
return {
|
|
3452
|
-
id: options.id,
|
|
3453
|
-
targetLoopId: "*",
|
|
3454
|
-
install: (context) => {
|
|
3455
|
-
const registry = getToolRegistry(context);
|
|
3456
|
-
if (!registry) return;
|
|
3457
|
-
const builtinContext = context;
|
|
3458
|
-
registry.registerTool(options.toolId, (arguments_) => options.implementation(arguments_, builtinContext));
|
|
3459
|
-
registerToolParameterSchema(options.toolId, options.schema, options.metadata);
|
|
3460
|
-
}
|
|
3461
|
-
};
|
|
3462
|
-
}
|
|
3463
|
-
var builtinToolPlugins = [
|
|
3464
|
-
createBuiltinToolPlugin({
|
|
3465
|
-
id: PLUGIN_MCP_CLIENT,
|
|
3466
|
-
toolId: getMcpClientToolId(),
|
|
3467
|
-
schema: mcpClientConfigSchema,
|
|
3468
|
-
metadata: {
|
|
3469
|
-
displayName: "MCP Client",
|
|
3470
|
-
description: "Call a tool on a remote MCP server (transparent proxy). Requires nodeId, serverName, toolName, and optional args."
|
|
3471
|
-
},
|
|
3472
|
-
implementation: mcpClientImpl
|
|
3473
|
-
}),
|
|
3474
|
-
createBuiltinToolPlugin({
|
|
3475
|
-
id: PLUGIN_MCP_FORWARD,
|
|
3476
|
-
toolId: getMcpForwardToolId(),
|
|
3477
|
-
schema: mcpForwardConfigSchema,
|
|
3478
|
-
metadata: {
|
|
3479
|
-
displayName: "MCP Forward",
|
|
3480
|
-
description: "Discover MCP servers and tools on connected nodes. Use action='list' for nodes with servers, action='listTools' for all available tools."
|
|
3481
|
-
},
|
|
3482
|
-
implementation: mcpForwardImpl
|
|
3483
|
-
}),
|
|
3484
|
-
createBuiltinToolPlugin({
|
|
3485
|
-
id: PLUGIN_SPAWN_AGENT,
|
|
3486
|
-
toolId: getSpawnAgentToolId(),
|
|
3487
|
-
schema: spawnAgentConfigSchema,
|
|
3488
|
-
metadata: {
|
|
3489
|
-
displayName: "Spawn Agent",
|
|
3490
|
-
description: "Run a local agent-agent-loop with the given definition and message. Returns summary."
|
|
3491
|
-
},
|
|
3492
|
-
implementation: spawnAgentImpl
|
|
3493
|
-
}),
|
|
3494
|
-
createBuiltinToolPlugin({
|
|
3495
|
-
id: PLUGIN_REMOTE_AGENT,
|
|
3496
|
-
toolId: getRemoteAgentToolId(),
|
|
3497
|
-
schema: remoteAgentConfigSchema,
|
|
3498
|
-
metadata: {
|
|
3499
|
-
displayName: "Remote Agent",
|
|
3500
|
-
description: "Create and run a agent-agent-loop on a remote node. Requires nodeId, definitionId, message. List nodes with no args."
|
|
3501
|
-
},
|
|
3502
|
-
implementation: remoteAgentImpl
|
|
3503
|
-
}),
|
|
3504
|
-
createBuiltinToolPlugin({
|
|
3505
|
-
id: PLUGIN_ASK_QUESTION,
|
|
3506
|
-
toolId: ASK_QUESTION_TOOL_ID,
|
|
3507
|
-
schema: askQuestionConfigSchema,
|
|
3508
|
-
metadata: {
|
|
3509
|
-
displayName: "Ask Question",
|
|
3510
|
-
description: "Block until the user answers (via memeloop.agent.resolveQuestion RPC). Args: question, conversationId, optional timeoutMs."
|
|
3511
|
-
},
|
|
3512
|
-
implementation: askQuestionImpl
|
|
3513
|
-
}),
|
|
3514
|
-
createBuiltinToolPlugin({
|
|
3515
|
-
id: PLUGIN_TODO_WRITE,
|
|
3516
|
-
toolId: TODO_WRITE_TOOL_ID,
|
|
3517
|
-
schema: todoWriteConfigSchema,
|
|
3518
|
-
metadata: {
|
|
3519
|
-
displayName: "Todo Write",
|
|
3520
|
-
description: "Manage structured todo lists: create, update, complete, list, remove. Todos scoped per conversation."
|
|
3521
|
-
},
|
|
3522
|
-
implementation: todoWriteImpl
|
|
3523
|
-
}),
|
|
3524
|
-
createBuiltinToolPlugin({
|
|
3525
|
-
id: PLUGIN_ASK_USER_QUESTION,
|
|
3526
|
-
toolId: ASK_USER_QUESTION_TOOL_ID,
|
|
3527
|
-
schema: askUserQuestionConfigSchema,
|
|
3528
|
-
metadata: {
|
|
3529
|
-
displayName: "Ask User Question",
|
|
3530
|
-
description: "Pause agent and ask user a question. Supports text, single-select, and multi-select input types. Blocks until answered."
|
|
3531
|
-
},
|
|
3532
|
-
implementation: askUserQuestionImpl
|
|
3533
|
-
}),
|
|
3534
|
-
createBuiltinToolPlugin({
|
|
3535
|
-
id: PLUGIN_TASK,
|
|
3536
|
-
toolId: getTaskToolId(),
|
|
3537
|
-
schema: taskToolConfigSchema,
|
|
3538
|
-
metadata: {
|
|
3539
|
-
displayName: "Task Delegation",
|
|
3540
|
-
description: "Delegate a task to a specialized agent-agent-loop (build, plan, explore, oracle, librarian). Supports sync (default) and background modes."
|
|
3541
|
-
},
|
|
3542
|
-
implementation: taskToolImpl
|
|
3543
|
-
})
|
|
3544
|
-
];
|
|
3545
|
-
function getBuiltinToolPlugins() {
|
|
3546
|
-
return builtinToolPlugins;
|
|
3547
|
-
}
|
|
3548
|
-
function registerBuiltinToolPlugins() {
|
|
3549
|
-
const loopRegistry = getLoopRegistry();
|
|
3550
|
-
for (const plugin of builtinToolPlugins) {
|
|
3551
|
-
if (loopRegistry.getPlugin(plugin.id)) continue;
|
|
3552
|
-
loopRegistry.registerPlugin(plugin);
|
|
3553
|
-
}
|
|
3554
|
-
}
|
|
3555
|
-
|
|
3556
|
-
// src/loopAPI/tokenTracker.ts
|
|
3557
|
-
var TokenTracker = class {
|
|
3558
|
-
promptTokens = 0;
|
|
3559
|
-
completionTokens = 0;
|
|
3560
|
-
contextWindow;
|
|
3561
|
-
compactionThreshold;
|
|
3562
|
-
outputBuffer;
|
|
3563
|
-
compacting = false;
|
|
3564
|
-
consecutiveCompactionFailures = 0;
|
|
3565
|
-
maxConsecutiveFailures = 3;
|
|
3566
|
-
constructor(config) {
|
|
3567
|
-
this.contextWindow = config?.contextWindow ?? 2e5;
|
|
3568
|
-
this.compactionThreshold = config?.compactionThreshold ?? 0.9;
|
|
3569
|
-
this.outputBuffer = config?.outputBuffer ?? 16e3;
|
|
3570
|
-
}
|
|
3571
|
-
/** Record token usage from an LLM response */
|
|
3572
|
-
recordUsage(promptTokens, completionTokens) {
|
|
3573
|
-
this.promptTokens += promptTokens;
|
|
3574
|
-
this.completionTokens += completionTokens;
|
|
3575
|
-
}
|
|
3576
|
-
/** Manually set the prompt token count (e.g., from estimation) */
|
|
3577
|
-
setPromptTokens(tokens) {
|
|
3578
|
-
this.promptTokens = tokens;
|
|
3579
|
-
}
|
|
3580
|
-
/** Get current usage */
|
|
3581
|
-
getUsage() {
|
|
3582
|
-
const total = this.promptTokens + this.completionTokens;
|
|
3583
|
-
const effectiveWindow = this.contextWindow - this.outputBuffer;
|
|
3584
|
-
const usagePercent = total / effectiveWindow;
|
|
3585
|
-
return {
|
|
3586
|
-
promptTokens: this.promptTokens,
|
|
3587
|
-
completionTokens: this.completionTokens,
|
|
3588
|
-
totalTokens: total,
|
|
3589
|
-
contextWindow: this.contextWindow,
|
|
3590
|
-
usagePercent: Math.min(usagePercent, 1),
|
|
3591
|
-
shouldCompact: usagePercent >= this.compactionThreshold && !this.compacting
|
|
3592
|
-
};
|
|
3593
|
-
}
|
|
3594
|
-
/** Check if auto-compaction should be triggered */
|
|
3595
|
-
shouldTriggerCompaction() {
|
|
3596
|
-
if (this.consecutiveCompactionFailures >= this.maxConsecutiveFailures) return false;
|
|
3597
|
-
return this.getUsage().shouldCompact;
|
|
3598
|
-
}
|
|
3599
|
-
/** Mark that compaction is starting */
|
|
3600
|
-
markCompactionStart() {
|
|
3601
|
-
this.compacting = true;
|
|
3602
|
-
}
|
|
3603
|
-
/** Mark that compaction succeeded — reset token counts */
|
|
3604
|
-
markCompactionSuccess(estimatedPostTokens) {
|
|
3605
|
-
this.compacting = false;
|
|
3606
|
-
this.promptTokens = estimatedPostTokens;
|
|
3607
|
-
this.completionTokens = 0;
|
|
3608
|
-
this.consecutiveCompactionFailures = 0;
|
|
3609
|
-
}
|
|
3610
|
-
/** Mark that compaction failed */
|
|
3611
|
-
markCompactionFailure() {
|
|
3612
|
-
this.compacting = false;
|
|
3613
|
-
this.consecutiveCompactionFailures++;
|
|
3614
|
-
}
|
|
3615
|
-
/** Reset all counters */
|
|
3616
|
-
reset() {
|
|
3617
|
-
this.promptTokens = 0;
|
|
3618
|
-
this.completionTokens = 0;
|
|
3619
|
-
this.compacting = false;
|
|
3620
|
-
this.consecutiveCompactionFailures = 0;
|
|
3621
|
-
}
|
|
3622
|
-
/** Get a human-readable summary */
|
|
3623
|
-
formatUsage() {
|
|
3624
|
-
const usage = this.getUsage();
|
|
3625
|
-
const promptK = (usage.promptTokens / 1e3).toFixed(1);
|
|
3626
|
-
const windowK = (usage.contextWindow / 1e3).toFixed(0);
|
|
3627
|
-
const pct = (usage.usagePercent * 100).toFixed(1);
|
|
3628
|
-
return `${promptK}K/${windowK}K tokens (${pct}%)`;
|
|
3629
|
-
}
|
|
3630
|
-
};
|
|
3631
|
-
|
|
3632
|
-
// src/loopProfiles/builtinProfileSources.ts
|
|
3633
|
-
var builtinProfileSources = {
|
|
3634
|
-
"code-assistant": [
|
|
3635
|
-
"{",
|
|
3636
|
-
' "id": "memeloop:code-assistant",',
|
|
3637
|
-
' "name": "\u4EE3\u7801\u52A9\u624B",',
|
|
3638
|
-
' "description": "\u4E13\u6CE8\u4E8E\u4EE3\u7801\u7F16\u5199\u3001\u91CD\u6784\u548C\u8C03\u8BD5\u7684\u52A9\u624B\u3002",',
|
|
3639
|
-
' "loopId": "agent-tool-loop",',
|
|
3640
|
-
' "systemPrompt": "You are a senior software engineer helping the user write and refactor code.",',
|
|
3641
|
-
' "tools": [',
|
|
3642
|
-
' "workspacesList",',
|
|
3643
|
-
' "wikiSearch",',
|
|
3644
|
-
' "wikiOperation",',
|
|
3645
|
-
' "modelContextProtocol",',
|
|
3646
|
-
' "spawnAgent",',
|
|
3647
|
-
' "askQuestion",',
|
|
3648
|
-
' "getErrors",',
|
|
3649
|
-
' "webFetch"',
|
|
3650
|
-
" ],",
|
|
3651
|
-
' "plugins": [',
|
|
3652
|
-
' { "id": "builtin:mcp-client" },',
|
|
3653
|
-
' { "id": "builtin:mcp-forward" },',
|
|
3654
|
-
' { "id": "builtin:spawn-agent" },',
|
|
3655
|
-
' { "id": "builtin:ask-question" }',
|
|
3656
|
-
" ],",
|
|
3657
|
-
' "agentTools": [',
|
|
3658
|
-
" {",
|
|
3659
|
-
' "toolId": "workspacesList",',
|
|
3660
|
-
' "parameters": {',
|
|
3661
|
-
' "workspacesListParam": { "targetId": "builtin-system", "position": "after" }',
|
|
3662
|
-
" }",
|
|
3663
|
-
" },",
|
|
3664
|
-
" {",
|
|
3665
|
-
' "toolId": "wikiSearch",',
|
|
3666
|
-
' "parameters": {',
|
|
3667
|
-
' "wikiSearchParam": {',
|
|
3668
|
-
' "sourceType": "wiki",',
|
|
3669
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3670
|
-
' "toolResultDuration": 1',
|
|
3671
|
-
" }",
|
|
3672
|
-
" }",
|
|
3673
|
-
" },",
|
|
3674
|
-
" {",
|
|
3675
|
-
' "toolId": "wikiOperation",',
|
|
3676
|
-
' "parameters": {',
|
|
3677
|
-
' "wikiOperationParam": {',
|
|
3678
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3679
|
-
' "toolResultDuration": 1',
|
|
3680
|
-
" }",
|
|
3681
|
-
" }",
|
|
3682
|
-
" },",
|
|
3683
|
-
" {",
|
|
3684
|
-
' "toolId": "modelContextProtocol",',
|
|
3685
|
-
' "parameters": {',
|
|
3686
|
-
' "modelContextProtocolParam": {',
|
|
3687
|
-
' "serverUrl": "http://127.0.0.1:38385/mcp",',
|
|
3688
|
-
' "timeoutSecond": 30,',
|
|
3689
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3690
|
-
' "toolResultDuration": 1',
|
|
3691
|
-
" }",
|
|
3692
|
-
" }",
|
|
3693
|
-
" },",
|
|
3694
|
-
" {",
|
|
3695
|
-
' "toolId": "spawnAgent",',
|
|
3696
|
-
' "parameters": {',
|
|
3697
|
-
' "spawnAgentParam": {',
|
|
3698
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3699
|
-
' "toolResultDuration": 2,',
|
|
3700
|
-
' "defaultTimeoutMs": 120000',
|
|
3701
|
-
" }",
|
|
3702
|
-
" }",
|
|
3703
|
-
" },",
|
|
3704
|
-
" {",
|
|
3705
|
-
' "toolId": "askQuestion",',
|
|
3706
|
-
' "parameters": {',
|
|
3707
|
-
' "askQuestionParam": {',
|
|
3708
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" }',
|
|
3709
|
-
" }",
|
|
3710
|
-
" }",
|
|
3711
|
-
" },",
|
|
3712
|
-
" {",
|
|
3713
|
-
' "toolId": "getErrors",',
|
|
3714
|
-
' "parameters": {',
|
|
3715
|
-
' "getErrorsParam": {',
|
|
3716
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" }',
|
|
3717
|
-
" }",
|
|
3718
|
-
" }",
|
|
3719
|
-
" },",
|
|
3720
|
-
" {",
|
|
3721
|
-
' "toolId": "webFetch",',
|
|
3722
|
-
' "parameters": {',
|
|
3723
|
-
' "webFetchParam": {',
|
|
3724
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3725
|
-
' "toolResultDuration": 1',
|
|
3726
|
-
" }",
|
|
3727
|
-
" }",
|
|
3728
|
-
" }",
|
|
3729
|
-
" ],",
|
|
3730
|
-
' "agentFrameworkConfig": {',
|
|
3731
|
-
' "prompts": [',
|
|
3732
|
-
" {",
|
|
3733
|
-
' "id": "builtin-system",',
|
|
3734
|
-
' "role": "system",',
|
|
3735
|
-
' "text": "You are a senior software engineer helping the user write and refactor code. Prefer small, safe edits; use file.read and file.search before changing code; use terminal.execute for build/tests when appropriate."',
|
|
3736
|
-
" }",
|
|
3737
|
-
" ],",
|
|
3738
|
-
' "plugins": [{ "toolId": "fullReplacement" }],',
|
|
3739
|
-
' "response": []',
|
|
3740
|
-
" },",
|
|
3741
|
-
' "modelConfig": {',
|
|
3742
|
-
' "provider": "memeloop",',
|
|
3743
|
-
' "model": "claude-opus-4.6",',
|
|
3744
|
-
' "temperature": 0.2,',
|
|
3745
|
-
' "maxTokens": 4096',
|
|
3746
|
-
" },",
|
|
3747
|
-
' "version": "1.0.0"',
|
|
3748
|
-
"}",
|
|
3749
|
-
""
|
|
3750
|
-
].join("\n"),
|
|
3751
|
-
"frontend-ui-ux": [
|
|
3752
|
-
"{",
|
|
3753
|
-
' "id": "memeloop:frontend-ui-ux",',
|
|
3754
|
-
' "name": "Frontend UI/UX",',
|
|
3755
|
-
' "description": "Designer-turned-developer who crafts stunning UI/UX even without design mockups.",',
|
|
3756
|
-
' "loopId": "agent-tool-loop",',
|
|
3757
|
-
' "systemPrompt": "You are a designer-turned-developer who crafts stunning UI/UX even without design mockups.\\n\\nWhen working on frontend tasks:\\n- Prioritize visual polish and user experience above all else\\n- Use consistent spacing, typography, and color schemes\\n- Apply modern design patterns: rounded corners, subtle shadows, smooth transitions\\n- Ensure responsive design across mobile, tablet, and desktop breakpoints\\n- Prefer accessible patterns: semantic HTML, ARIA labels, keyboard navigation\\n- Optimize for perceived performance: skeleton loaders, optimistic updates, progressive enhancement\\n- Use component composition over monolithic layouts\\n- Match the existing design system when extending an existing UI",',
|
|
3758
|
-
' "tools": [',
|
|
3759
|
-
' "workspacesList",',
|
|
3760
|
-
' "wikiSearch",',
|
|
3761
|
-
' "wikiOperation",',
|
|
3762
|
-
' "modelContextProtocol",',
|
|
3763
|
-
' "spawnAgent",',
|
|
3764
|
-
' "askQuestion",',
|
|
3765
|
-
' "webFetch"',
|
|
3766
|
-
" ],",
|
|
3767
|
-
' "plugins": [',
|
|
3768
|
-
' { "id": "builtin:mcp-client" },',
|
|
3769
|
-
' { "id": "builtin:mcp-forward" },',
|
|
3770
|
-
' { "id": "builtin:spawn-agent" },',
|
|
3771
|
-
' { "id": "builtin:ask-question" }',
|
|
3772
|
-
" ],",
|
|
3773
|
-
' "agentTools": [',
|
|
3774
|
-
" {",
|
|
3775
|
-
' "toolId": "workspacesList",',
|
|
3776
|
-
' "parameters": {',
|
|
3777
|
-
' "workspacesListParam": { "targetId": "builtin-system", "position": "after" }',
|
|
3778
|
-
" }",
|
|
3779
|
-
" },",
|
|
3780
|
-
" {",
|
|
3781
|
-
' "toolId": "wikiSearch",',
|
|
3782
|
-
' "parameters": {',
|
|
3783
|
-
' "wikiSearchParam": {',
|
|
3784
|
-
' "sourceType": "wiki",',
|
|
3785
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3786
|
-
' "toolResultDuration": 1',
|
|
3787
|
-
" }",
|
|
3788
|
-
" }",
|
|
3789
|
-
" },",
|
|
3790
|
-
" {",
|
|
3791
|
-
' "toolId": "wikiOperation",',
|
|
3792
|
-
' "parameters": {',
|
|
3793
|
-
' "wikiOperationParam": {',
|
|
3794
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3795
|
-
' "toolResultDuration": 1',
|
|
3796
|
-
" }",
|
|
3797
|
-
" }",
|
|
3798
|
-
" },",
|
|
3799
|
-
" {",
|
|
3800
|
-
' "toolId": "modelContextProtocol",',
|
|
3801
|
-
' "parameters": {',
|
|
3802
|
-
' "modelContextProtocolParam": {',
|
|
3803
|
-
' "serverUrl": "http://127.0.0.1:38385/mcp",',
|
|
3804
|
-
' "timeoutSecond": 30,',
|
|
3805
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3806
|
-
' "toolResultDuration": 1',
|
|
3807
|
-
" }",
|
|
3808
|
-
" }",
|
|
3809
|
-
" },",
|
|
3810
|
-
" {",
|
|
3811
|
-
' "toolId": "spawnAgent",',
|
|
3812
|
-
' "parameters": {',
|
|
3813
|
-
' "spawnAgentParam": {',
|
|
3814
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3815
|
-
' "toolResultDuration": 2,',
|
|
3816
|
-
' "defaultTimeoutMs": 120000',
|
|
3817
|
-
" }",
|
|
3818
|
-
" }",
|
|
3819
|
-
" },",
|
|
3820
|
-
" {",
|
|
3821
|
-
' "toolId": "askQuestion",',
|
|
3822
|
-
' "parameters": {',
|
|
3823
|
-
' "askQuestionParam": {',
|
|
3824
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" }',
|
|
3825
|
-
" }",
|
|
3826
|
-
" }",
|
|
3827
|
-
" },",
|
|
3828
|
-
" {",
|
|
3829
|
-
' "toolId": "webFetch",',
|
|
3830
|
-
' "parameters": {',
|
|
3831
|
-
' "webFetchParam": {',
|
|
3832
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3833
|
-
' "toolResultDuration": 1',
|
|
3834
|
-
" }",
|
|
3835
|
-
" }",
|
|
3836
|
-
" }",
|
|
3837
|
-
" ],",
|
|
3838
|
-
' "agentFrameworkConfig": {',
|
|
3839
|
-
' "prompts": [',
|
|
3840
|
-
" {",
|
|
3841
|
-
' "id": "builtin-system",',
|
|
3842
|
-
' "role": "system",',
|
|
3843
|
-
' "text": "You are a designer-turned-developer who crafts stunning UI/UX even without design mockups.\\n\\nWhen working on frontend tasks:\\n- Prioritize visual polish and user experience above all else\\n- Use consistent spacing, typography, and color schemes\\n- Apply modern design patterns: rounded corners, subtle shadows, smooth transitions\\n- Ensure responsive design across mobile, tablet, and desktop breakpoints\\n- Prefer accessible patterns: semantic HTML, ARIA labels, keyboard navigation\\n- Optimize for perceived performance: skeleton loaders, optimistic updates, progressive enhancement\\n- Use component composition over monolithic layouts\\n- Match the existing design system when extending an existing UI"',
|
|
3844
|
-
" }",
|
|
3845
|
-
" ],",
|
|
3846
|
-
' "plugins": [],',
|
|
3847
|
-
' "response": []',
|
|
3848
|
-
" },",
|
|
3849
|
-
' "modelConfig": {',
|
|
3850
|
-
' "provider": "memeloop",',
|
|
3851
|
-
' "model": "claude-opus-4.6",',
|
|
3852
|
-
' "temperature": 0.3,',
|
|
3853
|
-
' "maxTokens": 4096',
|
|
3854
|
-
" },",
|
|
3855
|
-
' "version": "1.0.0"',
|
|
3856
|
-
"}",
|
|
3857
|
-
""
|
|
3858
|
-
].join("\n"),
|
|
3859
|
-
"general-assistant": [
|
|
3860
|
-
"{",
|
|
3861
|
-
' "id": "memeloop:general-assistant",',
|
|
3862
|
-
' "name": "\u901A\u7528\u52A9\u624B",',
|
|
3863
|
-
' "description": "\u901A\u7528\u81EA\u7136\u8BED\u8A00\u52A9\u7406\uFF0C\u7528\u4E8E\u65E5\u5E38\u95EE\u7B54\u548C\u7B80\u5355\u4EFB\u52A1\u3002",',
|
|
3864
|
-
' "loopId": "agent-tool-loop",',
|
|
3865
|
-
' "systemPrompt": "You are a helpful general-purpose assistant. When you need to use a tool, output ONLY the tool call in this exact XML format:\\n<tool_use name=\\"TOOL_NAME\\">{\\"param\\":\\"value\\"}</tool_use>\\nNever wrap tool calls in markdown code blocks or add explanatory text before/after the tool call in the same message. After receiving the tool result, continue responding normally.",',
|
|
3866
|
-
' "tools": [',
|
|
3867
|
-
' "workspacesList",',
|
|
3868
|
-
' "wikiSearch",',
|
|
3869
|
-
' "wikiOperation",',
|
|
3870
|
-
' "modelContextProtocol",',
|
|
3871
|
-
' "spawnAgent",',
|
|
3872
|
-
' "askQuestion"',
|
|
3873
|
-
" ],",
|
|
3874
|
-
' "plugins": [',
|
|
3875
|
-
' { "id": "builtin:mcp-client" },',
|
|
3876
|
-
' { "id": "builtin:mcp-forward" },',
|
|
3877
|
-
' { "id": "builtin:spawn-agent" },',
|
|
3878
|
-
' { "id": "builtin:ask-question" }',
|
|
3879
|
-
" ],",
|
|
3880
|
-
' "agentTools": [',
|
|
3881
|
-
" {",
|
|
3882
|
-
' "toolId": "workspacesList",',
|
|
3883
|
-
' "parameters": {',
|
|
3884
|
-
' "workspacesListParam": { "targetId": "builtin-system", "position": "after" }',
|
|
3885
|
-
" }",
|
|
3886
|
-
" },",
|
|
3887
|
-
" {",
|
|
3888
|
-
' "toolId": "wikiSearch",',
|
|
3889
|
-
' "parameters": {',
|
|
3890
|
-
' "wikiSearchParam": {',
|
|
3891
|
-
' "sourceType": "wiki",',
|
|
3892
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3893
|
-
' "toolResultDuration": 1',
|
|
3894
|
-
" }",
|
|
3895
|
-
" }",
|
|
3896
|
-
" },",
|
|
3897
|
-
" {",
|
|
3898
|
-
' "toolId": "wikiOperation",',
|
|
3899
|
-
' "parameters": {',
|
|
3900
|
-
' "wikiOperationParam": {',
|
|
3901
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3902
|
-
' "toolResultDuration": 1',
|
|
3903
|
-
" }",
|
|
3904
|
-
" }",
|
|
3905
|
-
" },",
|
|
3906
|
-
" {",
|
|
3907
|
-
' "toolId": "modelContextProtocol",',
|
|
3908
|
-
' "parameters": {',
|
|
3909
|
-
' "modelContextProtocolParam": {',
|
|
3910
|
-
' "serverUrl": "http://127.0.0.1:38385/mcp",',
|
|
3911
|
-
' "timeoutSecond": 30,',
|
|
3912
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3913
|
-
' "toolResultDuration": 1',
|
|
3914
|
-
" }",
|
|
3915
|
-
" }",
|
|
3916
|
-
" },",
|
|
3917
|
-
" {",
|
|
3918
|
-
' "toolId": "spawnAgent",',
|
|
3919
|
-
' "parameters": {',
|
|
3920
|
-
' "spawnAgentParam": {',
|
|
3921
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
3922
|
-
' "toolResultDuration": 2,',
|
|
3923
|
-
' "defaultTimeoutMs": 120000',
|
|
3924
|
-
" }",
|
|
3925
|
-
" }",
|
|
3926
|
-
" },",
|
|
3927
|
-
" {",
|
|
3928
|
-
' "toolId": "askQuestion",',
|
|
3929
|
-
' "parameters": {',
|
|
3930
|
-
' "askQuestionParam": {',
|
|
3931
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" }',
|
|
3932
|
-
" }",
|
|
3933
|
-
" }",
|
|
3934
|
-
" }",
|
|
3935
|
-
" ],",
|
|
3936
|
-
' "agentFrameworkConfig": {',
|
|
3937
|
-
' "prompts": [',
|
|
3938
|
-
" {",
|
|
3939
|
-
' "id": "builtin-system",',
|
|
3940
|
-
' "role": "system",',
|
|
3941
|
-
' "text": "You are a helpful general-purpose assistant. When you need to use a tool, output ONLY the tool call in this exact XML format:\\n<tool_use name=\\"TOOL_NAME\\">{\\"param\\":\\"value\\"}</tool_use>\\nNever wrap tool calls in markdown code blocks or add explanatory text before/after the tool call in the same message. After receiving the tool result, continue responding normally."',
|
|
3942
|
-
" }",
|
|
3943
|
-
" ],",
|
|
3944
|
-
' "plugins": [{ "toolId": "fullReplacement" }],',
|
|
3945
|
-
' "response": []',
|
|
3946
|
-
" },",
|
|
3947
|
-
' "modelConfig": {',
|
|
3948
|
-
' "provider": "memeloop",',
|
|
3949
|
-
' "model": "claude-opus-4.6",',
|
|
3950
|
-
' "temperature": 0.5,',
|
|
3951
|
-
' "maxTokens": 4096',
|
|
3952
|
-
" },",
|
|
3953
|
-
' "version": "1.0.0"',
|
|
3954
|
-
"}",
|
|
3955
|
-
""
|
|
3956
|
-
].join("\n"),
|
|
3957
|
-
"git-master": [
|
|
3958
|
-
"{",
|
|
3959
|
-
' "id": "memeloop:git-master",',
|
|
3960
|
-
' "name": "Git Master",',
|
|
3961
|
-
' "description": "Expert git workflow management with safety-first approach.",',
|
|
3962
|
-
' "loopId": "agent-tool-loop",',
|
|
3963
|
-
' "systemPrompt": "You are an expert in git workflow management. Follow these rules:\\n\\nGit Safety Protocol:\\n- NEVER update the git config\\n- NEVER run destructive/irreversible git commands (push --force, hard reset, etc.) unless the user explicitly requests them\\n- NEVER skip hooks (--no-verify, --no-gpg-sign, etc.) unless the user explicitly requests it\\n- NEVER force push to main/master; warn the user if they request it\\n- Avoid git commit --amend. ONLY use --amend when ALL conditions are met:\\n (1) User explicitly requested amend, OR the commit succeeded and pre-commit hooks auto-modified files that need including\\n (2) HEAD commit was created by you in this conversation\\n (3) Commit has NOT been pushed to remote\\n- If commit FAILED or was REJECTED by hook, NEVER amend \u2014 fix the issue and create a NEW commit\\n- If you already pushed to remote, NEVER amend unless user explicitly requests it (requires force push)\\n\\nCommitting:\\n- NEVER commit changes unless the user explicitly asks\\n- Run: git status, git diff, git log (recent commits)\\n- Analyze all staged changes and draft a concise commit message\\n- Summarize nature of changes: add/update/fix/refactor/test/docs\\n- Do NOT commit files that likely contain secrets\\n\\nPull Requests:\\n- Check branch status, divergence from base, full commit history\\n- Create PR with descriptive title and summary body\\n- Return the PR URL when done",',
|
|
3964
|
-
' "tools": ["workspacesList", "git", "askQuestion"],',
|
|
3965
|
-
' "plugins": [{ "id": "builtin:ask-question" }],',
|
|
3966
|
-
' "agentTools": [',
|
|
3967
|
-
" {",
|
|
3968
|
-
' "toolId": "workspacesList",',
|
|
3969
|
-
' "parameters": {',
|
|
3970
|
-
' "workspacesListParam": { "targetId": "builtin-system", "position": "after" }',
|
|
3971
|
-
" }",
|
|
3972
|
-
" },",
|
|
3973
|
-
" {",
|
|
3974
|
-
' "toolId": "git",',
|
|
3975
|
-
' "parameters": {',
|
|
3976
|
-
' "gitParam": {',
|
|
3977
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" }',
|
|
3978
|
-
" }",
|
|
3979
|
-
" }",
|
|
3980
|
-
" },",
|
|
3981
|
-
" {",
|
|
3982
|
-
' "toolId": "askQuestion",',
|
|
3983
|
-
' "parameters": {',
|
|
3984
|
-
' "askQuestionParam": {',
|
|
3985
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" }',
|
|
3986
|
-
" }",
|
|
3987
|
-
" }",
|
|
3988
|
-
" }",
|
|
3989
|
-
" ],",
|
|
3990
|
-
' "agentFrameworkConfig": {',
|
|
3991
|
-
' "prompts": [',
|
|
3992
|
-
" {",
|
|
3993
|
-
' "id": "builtin-system",',
|
|
3994
|
-
' "role": "system",',
|
|
3995
|
-
' "text": "You are an expert in git workflow management. Follow these rules:\\n\\nGit Safety Protocol:\\n- NEVER update the git config\\n- NEVER run destructive/irreversible git commands (push --force, hard reset, etc.) unless the user explicitly requests them\\n- NEVER skip hooks (--no-verify, --no-gpg-sign, etc.) unless the user explicitly requests it\\n- NEVER force push to main/master; warn the user if they request it\\n- Avoid git commit --amend. ONLY use --amend when ALL conditions are met:\\n (1) User explicitly requested amend, OR the commit succeeded and pre-commit hooks auto-modified files that need including\\n (2) HEAD commit was created by you in this conversation\\n (3) Commit has NOT been pushed to remote\\n- If commit FAILED or was REJECTED by hook, NEVER amend \u2014 fix the issue and create a NEW commit\\n- If you already pushed to remote, NEVER amend unless user explicitly requests it (requires force push)\\n\\nCommitting:\\n- NEVER commit changes unless the user explicitly asks\\n- Run: git status, git diff, git log (recent commits)\\n- Analyze all staged changes and draft a concise commit message\\n- Summarize nature of changes: add/update/fix/refactor/test/docs\\n- Do NOT commit files that likely contain secrets\\n\\nPull Requests:\\n- Check branch status, divergence from base, full commit history\\n- Create PR with descriptive title and summary body\\n- Return the PR URL when done"',
|
|
3996
|
-
" }",
|
|
3997
|
-
" ],",
|
|
3998
|
-
' "plugins": [],',
|
|
3999
|
-
' "response": []',
|
|
4000
|
-
" },",
|
|
4001
|
-
' "modelConfig": {',
|
|
4002
|
-
' "provider": "memeloop",',
|
|
4003
|
-
' "model": "claude-opus-4.6",',
|
|
4004
|
-
' "temperature": 0.2,',
|
|
4005
|
-
' "maxTokens": 4096',
|
|
4006
|
-
" },",
|
|
4007
|
-
' "version": "1.0.0"',
|
|
4008
|
-
"}",
|
|
4009
|
-
""
|
|
4010
|
-
].join("\n"),
|
|
4011
|
-
"playwright": [
|
|
4012
|
-
"{",
|
|
4013
|
-
' "id": "memeloop:playwright",',
|
|
4014
|
-
' "name": "Playwright",',
|
|
4015
|
-
' "description": "Browser automation via Playwright for verification, browsing, information gathering, web scraping, testing, and screenshots.",',
|
|
4016
|
-
' "loopId": "agent-tool-loop",',
|
|
4017
|
-
' "systemPrompt": "You have access to Playwright for browser automation. Use it for:\\n\\nBrowser Automation:\\n- Navigate to URLs and verify page content\\n- Fill forms, click buttons, and interact with page elements\\n- Take screenshots of specific elements or full pages\\n- Extract data from web pages (scraping)\\n- Test web application functionality end-to-end\\n- Automate browser workflows\\n- Log into websites and maintain sessions\\n\\nBest Practices:\\n- Use specific selectors (data-testid, id, or unique CSS selectors) over fragile XPath\\n- Wait for elements to be visible before interacting\\n- Handle page navigation and loading states explicitly\\n- Take screenshots at key steps for debugging\\n- Clean up browser resources when done\\n- Handle timeouts and error states gracefully\\n\\nAvailable Playwright MCP Tools:\\n- browser_navigate: Navigate to a URL\\n- browser_click: Click an element\\n- browser_type: Type into an input field\\n- browser_snapshot: Take accessibility snapshot of page\\n- browser_take_screenshot: Capture screenshot\\n- browser_fill_form: Fill multiple form fields at once\\n- browser_evaluate: Execute JavaScript in page context\\n- browser_select_option: Select from dropdown\\n- browser_drag: Drag and drop elements\\n- browser_hover: Hover over an element\\n- browser_press_key: Press a keyboard key\\n- browser_handle_dialog: Handle browser dialogs (alert/confirm/prompt)\\n- browser_close: Close the browser",',
|
|
4018
|
-
' "tools": ["modelContextProtocol", "askQuestion"],',
|
|
4019
|
-
' "plugins": [',
|
|
4020
|
-
' { "id": "builtin:mcp-client" },',
|
|
4021
|
-
' { "id": "builtin:mcp-forward" },',
|
|
4022
|
-
' { "id": "builtin:ask-question" }',
|
|
4023
|
-
" ],",
|
|
4024
|
-
' "agentTools": [',
|
|
4025
|
-
" {",
|
|
4026
|
-
' "toolId": "modelContextProtocol",',
|
|
4027
|
-
' "parameters": {',
|
|
4028
|
-
' "modelContextProtocolParam": {',
|
|
4029
|
-
' "serverUrl": "http://127.0.0.1:38385/mcp",',
|
|
4030
|
-
' "timeoutSecond": 30,',
|
|
4031
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" },',
|
|
4032
|
-
' "toolResultDuration": 1',
|
|
4033
|
-
" }",
|
|
4034
|
-
" }",
|
|
4035
|
-
" },",
|
|
4036
|
-
" {",
|
|
4037
|
-
' "toolId": "askQuestion",',
|
|
4038
|
-
' "parameters": {',
|
|
4039
|
-
' "askQuestionParam": {',
|
|
4040
|
-
' "toolListPosition": { "targetId": "builtin-system", "position": "after" }',
|
|
4041
|
-
" }",
|
|
4042
|
-
" }",
|
|
4043
|
-
" }",
|
|
4044
|
-
" ],",
|
|
4045
|
-
' "agentFrameworkConfig": {',
|
|
4046
|
-
' "prompts": [',
|
|
4047
|
-
" {",
|
|
4048
|
-
' "id": "builtin-system",',
|
|
4049
|
-
' "role": "system",',
|
|
4050
|
-
' "text": "You have access to Playwright for browser automation. Use it for:\\n\\nBrowser Automation:\\n- Navigate to URLs and verify page content\\n- Fill forms, click buttons, and interact with page elements\\n- Take screenshots of specific elements or full pages\\n- Extract data from web pages (scraping)\\n- Test web application functionality end-to-end\\n- Automate browser workflows\\n- Log into websites and maintain sessions\\n\\nBest Practices:\\n- Use specific selectors (data-testid, id, or unique CSS selectors) over fragile XPath\\n- Wait for elements to be visible before interacting\\n- Handle page navigation and loading states explicitly\\n- Take screenshots at key steps for debugging\\n- Clean up browser resources when done\\n- Handle timeouts and error states gracefully\\n\\nAvailable Playwright MCP Tools:\\n- browser_navigate: Navigate to a URL\\n- browser_click: Click an element\\n- browser_type: Type into an input field\\n- browser_snapshot: Take accessibility snapshot of page\\n- browser_take_screenshot: Capture screenshot\\n- browser_fill_form: Fill multiple form fields at once\\n- browser_evaluate: Execute JavaScript in page context\\n- browser_select_option: Select from dropdown\\n- browser_drag: Drag and drop elements\\n- browser_hover: Hover over an element\\n- browser_press_key: Press a keyboard key\\n- browser_handle_dialog: Handle browser dialogs (alert/confirm/prompt)\\n- browser_close: Close the browser"',
|
|
4051
|
-
" }",
|
|
4052
|
-
" ],",
|
|
4053
|
-
' "plugins": [],',
|
|
4054
|
-
' "response": []',
|
|
4055
|
-
" },",
|
|
4056
|
-
' "modelConfig": {',
|
|
4057
|
-
' "provider": "memeloop",',
|
|
4058
|
-
' "model": "claude-opus-4.6",',
|
|
4059
|
-
' "temperature": 0.3,',
|
|
4060
|
-
' "maxTokens": 4096',
|
|
4061
|
-
" },",
|
|
4062
|
-
' "version": "1.0.0"',
|
|
4063
|
-
"}",
|
|
4064
|
-
""
|
|
4065
|
-
].join("\n")
|
|
4066
|
-
};
|
|
4067
|
-
|
|
4068
|
-
// src/loopProfiles/loadBuiltins.ts
|
|
4069
|
-
function loadProfile(name) {
|
|
4070
|
-
const source = builtinProfileSources[name];
|
|
4071
|
-
if (!source) {
|
|
4072
|
-
throw new Error(`Builtin profile not found: ${name}`);
|
|
4073
|
-
}
|
|
4074
|
-
return JSON.parse(source);
|
|
4075
|
-
}
|
|
4076
|
-
function getBuiltinLoopProfiles() {
|
|
4077
|
-
return [
|
|
4078
|
-
loadProfile("general-assistant"),
|
|
4079
|
-
loadProfile("code-assistant"),
|
|
4080
|
-
loadProfile("frontend-ui-ux"),
|
|
4081
|
-
loadProfile("git-master"),
|
|
4082
|
-
loadProfile("playwright")
|
|
4083
|
-
];
|
|
4084
|
-
}
|
|
4085
|
-
function getBuiltinLoopProfile(id) {
|
|
4086
|
-
return getBuiltinLoopProfiles().find((p) => p.id === id);
|
|
4087
|
-
}
|
|
4088
|
-
function registerBuiltinLoopProfiles() {
|
|
4089
|
-
const registry = getLoopRegistry();
|
|
4090
|
-
for (const profile of getBuiltinLoopProfiles()) {
|
|
4091
|
-
registry.registerProfile(profile);
|
|
4092
|
-
}
|
|
4093
|
-
}
|
|
4094
|
-
|
|
4095
|
-
// src/promptUtilities/builtinPromptPlugins.ts
|
|
4096
|
-
var FULL_REPLACEMENT_PLUGIN_TOOL_ID = "fullReplacement";
|
|
4097
|
-
var DYNAMIC_POSITION_PLUGIN_TOOL_ID = "dynamicPosition";
|
|
4098
|
-
function registerFullReplacement(reg) {
|
|
4099
|
-
if (reg.has(FULL_REPLACEMENT_PLUGIN_TOOL_ID)) return;
|
|
4100
|
-
reg.set(FULL_REPLACEMENT_PLUGIN_TOOL_ID, (hooks) => {
|
|
4101
|
-
hooks.processPrompts.tapAsync(
|
|
4102
|
-
"fullReplacementLite",
|
|
4103
|
-
(context, callback) => {
|
|
4104
|
-
const msgs = context.messages;
|
|
4105
|
-
if (!Array.isArray(msgs)) {
|
|
4106
|
-
callback();
|
|
4107
|
-
return;
|
|
4108
|
-
}
|
|
4109
|
-
const maxChars = Number(process.env.MEMELOOP_FULL_REPLACEMENT_MAX_CHARS ?? 48e3);
|
|
4110
|
-
if (!Number.isFinite(maxChars) || maxChars <= 0) {
|
|
4111
|
-
callback();
|
|
4112
|
-
return;
|
|
4113
|
-
}
|
|
4114
|
-
let total = 0;
|
|
4115
|
-
const kept = [];
|
|
4116
|
-
for (let index = msgs.length - 1; index >= 0; index--) {
|
|
4117
|
-
const m = msgs[index];
|
|
4118
|
-
const c = typeof m?.content === "string" ? m.content : m?.content != null ? JSON.stringify(m.content) : "";
|
|
4119
|
-
total += c.length;
|
|
4120
|
-
if (total > maxChars) break;
|
|
4121
|
-
kept.push(m);
|
|
4122
|
-
}
|
|
4123
|
-
kept.reverse();
|
|
4124
|
-
context.messages = kept;
|
|
4125
|
-
callback();
|
|
4126
|
-
}
|
|
4127
|
-
);
|
|
4128
|
-
});
|
|
4129
|
-
}
|
|
4130
|
-
function registerDynamicPosition(reg) {
|
|
4131
|
-
if (reg.has(DYNAMIC_POSITION_PLUGIN_TOOL_ID)) return;
|
|
4132
|
-
reg.set(DYNAMIC_POSITION_PLUGIN_TOOL_ID, (hooks) => {
|
|
4133
|
-
hooks.processPrompts.tapAsync(
|
|
4134
|
-
"dynamicPositionLite",
|
|
4135
|
-
(context, callback) => {
|
|
4136
|
-
const prompts = context.prompts;
|
|
4137
|
-
const messages = context.messages;
|
|
4138
|
-
if (!Array.isArray(prompts) || prompts.length < 2 || !Array.isArray(messages)) {
|
|
4139
|
-
callback();
|
|
4140
|
-
return;
|
|
4141
|
-
}
|
|
4142
|
-
const userTurns = messages.filter((m) => m?.role === "user").length;
|
|
4143
|
-
if (userTurns < 2) {
|
|
4144
|
-
callback();
|
|
4145
|
-
return;
|
|
4146
|
-
}
|
|
4147
|
-
const deferred = [];
|
|
4148
|
-
const rest = [];
|
|
4149
|
-
for (const n of prompts) {
|
|
4150
|
-
if (n?.dynamicPosition === "deferToEnd") deferred.push(n);
|
|
4151
|
-
else rest.push(n);
|
|
4152
|
-
}
|
|
4153
|
-
if (deferred.length === 0) {
|
|
4154
|
-
callback();
|
|
4155
|
-
return;
|
|
4156
|
-
}
|
|
4157
|
-
context.prompts = [...rest, ...deferred];
|
|
4158
|
-
callback();
|
|
4159
|
-
}
|
|
4160
|
-
);
|
|
4161
|
-
});
|
|
4162
|
-
}
|
|
4163
|
-
function registerBuiltinPromptPlugins(target) {
|
|
4164
|
-
const reg = target ?? getActivePluginRegistry();
|
|
4165
|
-
registerFullReplacement(reg);
|
|
4166
|
-
registerDynamicPosition(reg);
|
|
4167
|
-
}
|
|
4168
|
-
|
|
4169
|
-
// src/runtime.ts
|
|
4170
|
-
async function drainAgentLoop(gen, conversationId, notify) {
|
|
4171
|
-
try {
|
|
4172
|
-
for await (const step of gen) {
|
|
4173
|
-
notify(conversationId, { type: "agent-step", step });
|
|
4174
|
-
}
|
|
4175
|
-
notify(conversationId, { type: "agent-done" });
|
|
4176
|
-
} catch (error) {
|
|
4177
|
-
notify(conversationId, {
|
|
4178
|
-
type: "agent-error",
|
|
4179
|
-
error: error instanceof Error ? error.message : String(error)
|
|
4180
|
-
});
|
|
4181
|
-
}
|
|
4182
|
-
}
|
|
4183
|
-
function inferDefinitionIdFromConversationId(conversationId) {
|
|
4184
|
-
const parts = conversationId.split(":");
|
|
4185
|
-
if (parts.length >= 2) return parts.slice(0, -1).join(":");
|
|
4186
|
-
return conversationId;
|
|
4187
|
-
}
|
|
4188
|
-
async function resolveDefinitionId(context, conversationId) {
|
|
4189
|
-
try {
|
|
4190
|
-
const meta = await context.storage.getConversationMeta(conversationId);
|
|
4191
|
-
if (meta?.definitionId) return meta.definitionId;
|
|
4192
|
-
} catch {
|
|
4193
|
-
}
|
|
4194
|
-
return inferDefinitionIdFromConversationId(conversationId);
|
|
4195
|
-
}
|
|
4196
|
-
async function resolveLoopProfile(context, definitionId) {
|
|
4197
|
-
const definition = (context.resolveAgentDefinition ? await context.resolveAgentDefinition(definitionId) : null) ?? await context.storage.getAgentDefinition(definitionId) ?? getBuiltinLoopProfile(definitionId) ?? null;
|
|
4198
|
-
if (!definition) return null;
|
|
4199
|
-
return definition;
|
|
4200
|
-
}
|
|
4201
|
-
async function createProfileRunner(context, definitionId, runtime) {
|
|
4202
|
-
const profile = await resolveLoopProfile(context, definitionId);
|
|
4203
|
-
if (!profile) return null;
|
|
4204
|
-
registerBuiltinLoops();
|
|
4205
|
-
registerBuiltinToolPlugins();
|
|
4206
|
-
registerBuiltinPromptPlugins(context.tools.getPromptPlugins?.());
|
|
4207
|
-
const runnerContext = {
|
|
4208
|
-
...context,
|
|
4209
|
-
runtime,
|
|
4210
|
-
scriptPolicy: context.loopScriptPolicy,
|
|
4211
|
-
toolRegistry: context.tools
|
|
4212
|
-
};
|
|
4213
|
-
return getLoopRegistry().createRunnerForProfile(profile, runnerContext);
|
|
4214
|
-
}
|
|
4215
|
-
function createScriptRuntime(context, cancellation, scriptState, conversationId, parentConversationId) {
|
|
4216
|
-
const stateKey = (key) => `${conversationId}:${key}`;
|
|
4217
|
-
return {
|
|
4218
|
-
runChildAgent: async function* (input) {
|
|
4219
|
-
const childRuntime = createScriptRuntime(
|
|
4220
|
-
context,
|
|
4221
|
-
cancellation,
|
|
4222
|
-
scriptState,
|
|
4223
|
-
input.conversationId,
|
|
4224
|
-
conversationId
|
|
4225
|
-
);
|
|
4226
|
-
const run = await createProfileRunner(context, input.profileId, childRuntime);
|
|
4227
|
-
if (!run) {
|
|
4228
|
-
yield {
|
|
4229
|
-
type: "message",
|
|
4230
|
-
data: `Child agent profile not found: ${input.profileId}`
|
|
4231
|
-
};
|
|
4232
|
-
return;
|
|
4233
|
-
}
|
|
4234
|
-
yield* run({ conversationId: input.conversationId, message: input.prompt });
|
|
4235
|
-
},
|
|
4236
|
-
log: (event, data) => context.logger?.debug?.(event, data),
|
|
4237
|
-
emit: () => void 0,
|
|
4238
|
-
signal: {
|
|
4239
|
-
get cancelled() {
|
|
4240
|
-
return cancellation.has(conversationId) || (parentConversationId ? cancellation.has(parentConversationId) : false);
|
|
4241
|
-
}
|
|
4242
|
-
},
|
|
4243
|
-
state: {
|
|
4244
|
-
get: async (key) => scriptState.get(stateKey(key)),
|
|
4245
|
-
set: async (key, value) => {
|
|
4246
|
-
scriptState.set(stateKey(key), value);
|
|
4247
|
-
},
|
|
4248
|
-
update: async (key, updater) => {
|
|
4249
|
-
const fullKey = stateKey(key);
|
|
4250
|
-
scriptState.set(fullKey, updater(scriptState.get(fullKey)));
|
|
4251
|
-
}
|
|
4252
|
-
},
|
|
4253
|
-
checkpoint: async (key, result) => {
|
|
4254
|
-
scriptState.set(stateKey(`checkpoint:${key}`), result);
|
|
4255
|
-
}
|
|
4256
|
-
};
|
|
4257
|
-
}
|
|
4258
|
-
async function createAgentLoopRunner(context, options) {
|
|
4259
|
-
const cancellation = context.conversationCancellation ?? /* @__PURE__ */ new Set();
|
|
4260
|
-
context.conversationCancellation ??= cancellation;
|
|
4261
|
-
const scriptState = /* @__PURE__ */ new Map();
|
|
4262
|
-
const conversationId = options.conversationId ?? options.definitionId;
|
|
4263
|
-
return createProfileRunner(
|
|
4264
|
-
context,
|
|
4265
|
-
options.definitionId,
|
|
4266
|
-
createScriptRuntime(context, cancellation, scriptState, conversationId)
|
|
4267
|
-
);
|
|
4268
|
-
}
|
|
4269
|
-
function createMemeLoopRuntime(context) {
|
|
4270
|
-
const listeners = /* @__PURE__ */ new Map();
|
|
4271
|
-
const cancellation = context.conversationCancellation ?? /* @__PURE__ */ new Set();
|
|
4272
|
-
const scriptState = /* @__PURE__ */ new Map();
|
|
4273
|
-
context.conversationCancellation ??= cancellation;
|
|
4274
|
-
function notify(conversationId, update) {
|
|
4275
|
-
const set = listeners.get(conversationId);
|
|
4276
|
-
if (!set) return;
|
|
4277
|
-
for (const listener of set) {
|
|
4278
|
-
listener(update);
|
|
4279
|
-
}
|
|
4280
|
-
}
|
|
4281
|
-
async function runAgentLoop(input, definitionId) {
|
|
4282
|
-
const run = context.runAgentToolLoop ?? await createProfileRunner(
|
|
4283
|
-
context,
|
|
4284
|
-
definitionId,
|
|
4285
|
-
createScriptRuntime(context, cancellation, scriptState, input.conversationId)
|
|
4286
|
-
);
|
|
4287
|
-
if (!run) return false;
|
|
4288
|
-
void drainAgentLoop(run(input), input.conversationId, notify);
|
|
4289
|
-
return true;
|
|
4290
|
-
}
|
|
4291
|
-
async function* runChildAgent(input) {
|
|
4292
|
-
const run = await createProfileRunner(
|
|
4293
|
-
context,
|
|
4294
|
-
input.profileId,
|
|
4295
|
-
createScriptRuntime(context, cancellation, scriptState, input.conversationId)
|
|
4296
|
-
);
|
|
4297
|
-
if (!run) {
|
|
4298
|
-
yield {
|
|
4299
|
-
type: "message",
|
|
4300
|
-
data: `Child agent profile not found: ${input.profileId}`
|
|
4301
|
-
};
|
|
4302
|
-
return;
|
|
4303
|
-
}
|
|
4304
|
-
yield* run({ conversationId: input.conversationId, message: input.prompt });
|
|
4305
|
-
}
|
|
4306
|
-
context.runChildAgent ??= runChildAgent;
|
|
4307
|
-
return {
|
|
4308
|
-
async createAgent(options) {
|
|
4309
|
-
const now = Date.now();
|
|
4310
|
-
const conversationId = `${options.definitionId}:${now.toString(36)}`;
|
|
4311
|
-
cancellation?.delete(conversationId);
|
|
4312
|
-
const meta = {
|
|
4313
|
-
conversationId,
|
|
4314
|
-
title: options.definitionId,
|
|
4315
|
-
lastMessagePreview: context.runAgentToolLoop ? "" : options.initialMessage ?? "",
|
|
4316
|
-
lastMessageTimestamp: now,
|
|
4317
|
-
messageCount: context.runAgentToolLoop || !options.initialMessage ? 0 : 1,
|
|
4318
|
-
originNodeId: "local",
|
|
4319
|
-
definitionId: options.definitionId,
|
|
4320
|
-
isUserInitiated: true
|
|
4321
|
-
};
|
|
4322
|
-
await context.storage.upsertConversationMetadata(meta);
|
|
4323
|
-
if (options.initialMessage) {
|
|
4324
|
-
const started = await runAgentLoop(
|
|
4325
|
-
{ conversationId, message: options.initialMessage },
|
|
4326
|
-
options.definitionId
|
|
4327
|
-
);
|
|
4328
|
-
if (started) {
|
|
4329
|
-
notify(conversationId, { type: "created", conversationId });
|
|
4330
|
-
return { conversationId };
|
|
4331
|
-
}
|
|
4332
|
-
}
|
|
4333
|
-
if (options.initialMessage) {
|
|
4334
|
-
const message = {
|
|
4335
|
-
messageId: `${conversationId}:m1`,
|
|
4336
|
-
conversationId,
|
|
4337
|
-
originNodeId: meta.originNodeId,
|
|
4338
|
-
timestamp: now,
|
|
4339
|
-
lamportClock: 1,
|
|
4340
|
-
role: "user",
|
|
4341
|
-
content: options.initialMessage
|
|
4342
|
-
};
|
|
4343
|
-
await context.storage.appendMessage(message);
|
|
4344
|
-
}
|
|
4345
|
-
notify(conversationId, { type: "created", conversationId });
|
|
4346
|
-
return { conversationId };
|
|
4347
|
-
},
|
|
4348
|
-
async sendMessage(options) {
|
|
4349
|
-
cancellation?.delete(options.conversationId);
|
|
4350
|
-
const definitionId = options.definitionId ?? await resolveDefinitionId(context, options.conversationId);
|
|
4351
|
-
const started = await runAgentLoop(
|
|
4352
|
-
{
|
|
4353
|
-
conversationId: options.conversationId,
|
|
4354
|
-
message: options.message,
|
|
4355
|
-
userMessage: options.userMessage,
|
|
4356
|
-
resumeSession: options.resumeSession
|
|
4357
|
-
},
|
|
4358
|
-
definitionId
|
|
4359
|
-
);
|
|
4360
|
-
if (started) {
|
|
4361
|
-
notify(options.conversationId, { type: "message-queued" });
|
|
4362
|
-
return;
|
|
4363
|
-
}
|
|
4364
|
-
if (options.resumeSession && options.resumeSession.length > 0) {
|
|
4365
|
-
await context.storage.insertMessagesIfAbsent(options.resumeSession);
|
|
4366
|
-
}
|
|
4367
|
-
const now = Date.now();
|
|
4368
|
-
const lamportClock = await nextLamportClockForConversation(
|
|
4369
|
-
context.storage,
|
|
4370
|
-
options.conversationId
|
|
4371
|
-
);
|
|
4372
|
-
const hostUserMessage = options.userMessage;
|
|
4373
|
-
const message = {
|
|
4374
|
-
...hostUserMessage,
|
|
4375
|
-
messageId: hostUserMessage?.messageId ?? `${options.conversationId}:${now.toString(36)}`,
|
|
4376
|
-
conversationId: options.conversationId,
|
|
4377
|
-
originNodeId: hostUserMessage?.originNodeId ?? "local",
|
|
4378
|
-
timestamp: hostUserMessage?.timestamp ?? now,
|
|
4379
|
-
lamportClock: hostUserMessage?.lamportClock ?? lamportClock,
|
|
4380
|
-
role: "user",
|
|
4381
|
-
content: hostUserMessage?.content ?? options.message
|
|
4382
|
-
};
|
|
4383
|
-
await context.storage.appendMessage(message);
|
|
4384
|
-
notify(options.conversationId, { type: "message-queued" });
|
|
4385
|
-
},
|
|
4386
|
-
async cancelAgent(conversationId) {
|
|
4387
|
-
cancellation?.add(conversationId);
|
|
4388
|
-
notify(conversationId, { type: "cancelled" });
|
|
4389
|
-
},
|
|
4390
|
-
subscribeToUpdates(conversationId, listener) {
|
|
4391
|
-
const set = listeners.get(conversationId) ?? /* @__PURE__ */ new Set();
|
|
4392
|
-
set.add(listener);
|
|
4393
|
-
listeners.set(conversationId, set);
|
|
4394
|
-
return () => {
|
|
4395
|
-
const current = listeners.get(conversationId);
|
|
4396
|
-
if (!current) return;
|
|
4397
|
-
current.delete(listener);
|
|
4398
|
-
if (current.size === 0) {
|
|
4399
|
-
listeners.delete(conversationId);
|
|
4400
|
-
}
|
|
4401
|
-
};
|
|
4402
|
-
}
|
|
4403
|
-
};
|
|
4404
|
-
}
|
|
4405
|
-
|
|
4406
|
-
// src/agent/categories.ts
|
|
4407
|
-
var CATEGORY_CONFIGS = {
|
|
4408
|
-
"visual-engineering": {
|
|
4409
|
-
model: "default",
|
|
4410
|
-
temperature: 0.3,
|
|
4411
|
-
description: "UI/UX visual engineering tasks requiring precise rendering and layout"
|
|
4412
|
-
},
|
|
4413
|
-
ultrabrain: {
|
|
4414
|
-
model: "default",
|
|
4415
|
-
temperature: 0.1,
|
|
4416
|
-
description: "Deep logical reasoning, architecture decisions, and complex problem-solving"
|
|
4417
|
-
},
|
|
4418
|
-
artistry: {
|
|
4419
|
-
model: "default",
|
|
4420
|
-
temperature: 0.8,
|
|
4421
|
-
description: "Creative writing, design, and artistic generation tasks"
|
|
4422
|
-
},
|
|
4423
|
-
quick: {
|
|
4424
|
-
model: "default",
|
|
4425
|
-
temperature: 0.2,
|
|
4426
|
-
description: "Fast, straightforward tasks with minimal deliberation needed"
|
|
4427
|
-
},
|
|
4428
|
-
"unspecified-low": {
|
|
4429
|
-
model: "default",
|
|
4430
|
-
temperature: 0.5,
|
|
4431
|
-
description: "Unclassified tasks of moderate complexity and effort"
|
|
4432
|
-
},
|
|
4433
|
-
"unspecified-high": {
|
|
4434
|
-
model: "default",
|
|
4435
|
-
temperature: 0.4,
|
|
4436
|
-
description: "Unclassified tasks requiring substantial effort across multiple systems"
|
|
4437
|
-
},
|
|
4438
|
-
writing: {
|
|
4439
|
-
model: "default",
|
|
4440
|
-
temperature: 0.7,
|
|
4441
|
-
description: "Documentation, prose, technical writing, and content generation"
|
|
4442
|
-
}
|
|
4443
|
-
};
|
|
4444
|
-
function getCategoryConfig(category) {
|
|
4445
|
-
const config = CATEGORY_CONFIGS[category];
|
|
4446
|
-
if (!config) {
|
|
4447
|
-
throw new Error(`Unknown task category: ${category}`);
|
|
4448
|
-
}
|
|
4449
|
-
return { ...config };
|
|
4450
|
-
}
|
|
4451
|
-
function isTaskCategory(value) {
|
|
4452
|
-
return value in CATEGORY_CONFIGS;
|
|
4453
|
-
}
|
|
4454
|
-
var ALL_CATEGORIES = Object.keys(CATEGORY_CONFIGS);
|
|
4455
|
-
function resolveCategory(category) {
|
|
4456
|
-
if (isTaskCategory(category)) {
|
|
4457
|
-
return getCategoryConfig(category);
|
|
4458
|
-
}
|
|
4459
|
-
return getCategoryConfig("unspecified-low");
|
|
4460
|
-
}
|
|
4461
|
-
|
|
4462
|
-
// src/llm/providerRegistry.ts
|
|
4463
|
-
var ProviderRegistry = class {
|
|
4464
|
-
providers = /* @__PURE__ */ new Map();
|
|
4465
|
-
register(provider, config) {
|
|
4466
|
-
this.providers.set(provider.name, {
|
|
4467
|
-
provider,
|
|
4468
|
-
config: {
|
|
4469
|
-
name: provider.name,
|
|
4470
|
-
baseUrl: config?.baseUrl,
|
|
4471
|
-
apiKey: config?.apiKey
|
|
4472
|
-
}
|
|
4473
|
-
});
|
|
4474
|
-
}
|
|
4475
|
-
unregister(name) {
|
|
4476
|
-
this.providers.delete(name);
|
|
4477
|
-
}
|
|
4478
|
-
get(name) {
|
|
4479
|
-
return this.providers.get(name)?.provider;
|
|
4480
|
-
}
|
|
4481
|
-
getConfig(name) {
|
|
4482
|
-
return this.providers.get(name)?.config;
|
|
4483
|
-
}
|
|
4484
|
-
upsertConfig(config) {
|
|
4485
|
-
const existing = this.providers.get(config.name);
|
|
4486
|
-
if (!existing) {
|
|
4487
|
-
throw new Error(`Provider not found: ${config.name}`);
|
|
4488
|
-
}
|
|
4489
|
-
this.providers.set(config.name, {
|
|
4490
|
-
provider: existing.provider,
|
|
4491
|
-
config: {
|
|
4492
|
-
name: config.name,
|
|
4493
|
-
baseUrl: config.baseUrl,
|
|
4494
|
-
apiKey: config.apiKey
|
|
4495
|
-
}
|
|
4496
|
-
});
|
|
4497
|
-
}
|
|
4498
|
-
list() {
|
|
4499
|
-
return Array.from(this.providers.keys()).sort();
|
|
4500
|
-
}
|
|
4501
|
-
listConfigs() {
|
|
4502
|
-
return this.list().map((name) => {
|
|
4503
|
-
const config = this.providers.get(name)?.config;
|
|
4504
|
-
if (!config) {
|
|
4505
|
-
throw new Error(`Provider config missing: ${name}`);
|
|
4506
|
-
}
|
|
4507
|
-
return { ...config };
|
|
4508
|
-
});
|
|
4509
|
-
}
|
|
4510
|
-
resolve(modelId) {
|
|
4511
|
-
const [providerName, ...rest] = modelId.split("/");
|
|
4512
|
-
const registered = this.providers.get(providerName);
|
|
4513
|
-
if (!registered) {
|
|
4514
|
-
throw new Error(`Provider not found: ${providerName}`);
|
|
4515
|
-
}
|
|
4516
|
-
return {
|
|
4517
|
-
provider: registered.provider,
|
|
4518
|
-
providerName,
|
|
4519
|
-
modelName: rest.length > 0 ? rest.join("/") : void 0
|
|
4520
|
-
};
|
|
4521
|
-
}
|
|
4522
|
-
};
|
|
4523
|
-
|
|
4524
|
-
// src/conversation/factory.ts
|
|
4525
|
-
function createChatMessage(input) {
|
|
4526
|
-
const now = Date.now();
|
|
4527
|
-
return {
|
|
4528
|
-
messageId: input.messageId,
|
|
4529
|
-
conversationId: input.conversationId,
|
|
4530
|
-
originNodeId: input.originNodeId ?? "unknown",
|
|
4531
|
-
timestamp: now,
|
|
4532
|
-
lamportClock: input.lamportClock ?? now,
|
|
4533
|
-
role: input.role,
|
|
4534
|
-
content: input.content,
|
|
4535
|
-
contentType: input.contentType ?? "text/plain",
|
|
4536
|
-
metadata: input.metadata,
|
|
4537
|
-
duration: input.duration,
|
|
4538
|
-
toolCalls: input.toolCalls,
|
|
4539
|
-
reasoning_content: input.reasoning_content,
|
|
4540
|
-
hidden: input.hidden,
|
|
4541
|
-
attachments: input.attachments,
|
|
4542
|
-
detailRef: input.detailRef
|
|
4543
|
-
};
|
|
4544
|
-
}
|
|
4545
|
-
var DEFAULT_INSTANCE_STATUS = {
|
|
4546
|
-
state: "completed",
|
|
4547
|
-
modified: /* @__PURE__ */ new Date()
|
|
4548
|
-
};
|
|
4549
|
-
function createAgentInstanceFromDefinition(definition, overrides) {
|
|
4550
|
-
const now = /* @__PURE__ */ new Date();
|
|
4551
|
-
return {
|
|
4552
|
-
...definition,
|
|
4553
|
-
id: overrides.id,
|
|
4554
|
-
agentDefId: definition.id,
|
|
4555
|
-
name: overrides.name ?? definition.name,
|
|
4556
|
-
status: overrides.status ?? DEFAULT_INSTANCE_STATUS,
|
|
4557
|
-
messages: [],
|
|
4558
|
-
created: now,
|
|
4559
|
-
modified: now,
|
|
4560
|
-
closed: overrides.closed ?? false,
|
|
4561
|
-
volatile: overrides.volatile ?? false,
|
|
4562
|
-
isDelegatedAgentRun: overrides.isDelegatedAgentRun,
|
|
4563
|
-
parentAgentRunId: overrides.parentAgentRunId,
|
|
4564
|
-
agentFrameworkConfig: overrides.agentFrameworkConfig
|
|
4565
|
-
};
|
|
4566
|
-
}
|
|
4567
|
-
|
|
4568
|
-
// src/plugin/loader.ts
|
|
4569
|
-
var loadedPlugins = /* @__PURE__ */ new Map();
|
|
4570
|
-
var hookTypes = /* @__PURE__ */ new Set([
|
|
4571
|
-
"PreToolUse",
|
|
4572
|
-
"PostToolUse",
|
|
4573
|
-
"UserPromptSubmit",
|
|
4574
|
-
"ContextCompaction",
|
|
4575
|
-
"AgentStart",
|
|
4576
|
-
"AgentStop"
|
|
4577
|
-
]);
|
|
4578
|
-
function isHookType(value) {
|
|
4579
|
-
return typeof value === "string" && hookTypes.has(value);
|
|
4580
|
-
}
|
|
4581
|
-
function validatePluginManifest(object) {
|
|
4582
|
-
if (!object || typeof object !== "object") return null;
|
|
4583
|
-
const manifest = object;
|
|
4584
|
-
if (typeof manifest.name !== "string" || manifest.name.trim().length === 0 || typeof manifest.version !== "string" || manifest.version.trim().length === 0) {
|
|
4585
|
-
return null;
|
|
4586
|
-
}
|
|
4587
|
-
return {
|
|
4588
|
-
name: manifest.name.trim(),
|
|
4589
|
-
version: manifest.version.trim(),
|
|
4590
|
-
description: typeof manifest.description === "string" ? manifest.description : "",
|
|
4591
|
-
exports: validateExports(manifest.exports),
|
|
4592
|
-
author: typeof manifest.author === "string" ? manifest.author : void 0,
|
|
4593
|
-
minMemeloopVersion: typeof manifest.minMemeloopVersion === "string" ? manifest.minMemeloopVersion : void 0
|
|
4594
|
-
};
|
|
4595
|
-
}
|
|
4596
|
-
function validateExports(object) {
|
|
4597
|
-
if (!object || typeof object !== "object") return void 0;
|
|
4598
|
-
const exportsRecord = object;
|
|
4599
|
-
return {
|
|
4600
|
-
tools: Array.isArray(exportsRecord.tools) ? exportsRecord.tools.filter((tool) => typeof tool === "string") : void 0,
|
|
4601
|
-
hooks: Array.isArray(exportsRecord.hooks) ? exportsRecord.hooks.filter(isHookType) : void 0
|
|
4602
|
-
};
|
|
4603
|
-
}
|
|
4604
|
-
async function loadPluginModule(options) {
|
|
4605
|
-
const { manifest, module, api, source = "" } = options;
|
|
4606
|
-
const validManifest = validatePluginManifest(manifest);
|
|
4607
|
-
if (!validManifest) return null;
|
|
4608
|
-
if (loadedPlugins.has(validManifest.name)) {
|
|
4609
|
-
return loadedPlugins.get(validManifest.name) ?? null;
|
|
4610
|
-
}
|
|
4611
|
-
if (!module || typeof module.activate !== "function") {
|
|
4612
|
-
return null;
|
|
4613
|
-
}
|
|
4614
|
-
const cleanup = await module.activate(api);
|
|
4615
|
-
const loaded = {
|
|
4616
|
-
manifest: validManifest,
|
|
4617
|
-
source,
|
|
4618
|
-
module,
|
|
4619
|
-
cleanup: typeof cleanup === "function" ? cleanup : void 0,
|
|
4620
|
-
loadedAt: /* @__PURE__ */ new Date()
|
|
4621
|
-
};
|
|
4622
|
-
loadedPlugins.set(validManifest.name, loaded);
|
|
4623
|
-
return loaded;
|
|
4624
|
-
}
|
|
4625
|
-
async function loadPluginModules(plugins) {
|
|
4626
|
-
const loaded = [];
|
|
4627
|
-
for (const plugin of plugins) {
|
|
4628
|
-
const result = await loadPluginModule(plugin);
|
|
4629
|
-
if (result) loaded.push(result);
|
|
4630
|
-
}
|
|
4631
|
-
return loaded;
|
|
4632
|
-
}
|
|
4633
|
-
function unloadPlugin(name) {
|
|
4634
|
-
const loaded = loadedPlugins.get(name);
|
|
4635
|
-
if (!loaded) return false;
|
|
4636
|
-
try {
|
|
4637
|
-
loaded.cleanup?.();
|
|
4638
|
-
} catch (error) {
|
|
4639
|
-
console.warn(`[plugin] Error during cleanup of "${name}":`, error);
|
|
4640
|
-
}
|
|
4641
|
-
loadedPlugins.delete(name);
|
|
4642
|
-
return true;
|
|
4643
|
-
}
|
|
4644
|
-
function listPlugins() {
|
|
4645
|
-
return Array.from(loadedPlugins.values());
|
|
4646
|
-
}
|
|
4647
|
-
function getLoadedPlugin(name) {
|
|
4648
|
-
return loadedPlugins.get(name);
|
|
4649
|
-
}
|
|
4650
|
-
function isPluginLoaded(name) {
|
|
4651
|
-
return loadedPlugins.has(name);
|
|
4652
|
-
}
|
|
4653
|
-
function unloadAllPlugins() {
|
|
4654
|
-
for (const name of loadedPlugins.keys()) {
|
|
4655
|
-
unloadPlugin(name);
|
|
4656
|
-
}
|
|
4657
|
-
}
|
|
4658
|
-
|
|
4659
|
-
// src/plugin/registry.ts
|
|
4660
|
-
var PluginRegistryManager = class {
|
|
4661
|
-
pluginRegistrations = /* @__PURE__ */ new Map();
|
|
4662
|
-
ensureRegistration(pluginName) {
|
|
4663
|
-
const existing = this.pluginRegistrations.get(pluginName);
|
|
4664
|
-
if (existing) return existing;
|
|
4665
|
-
const reg = {
|
|
4666
|
-
pluginName,
|
|
4667
|
-
tools: [],
|
|
4668
|
-
hooks: []
|
|
4669
|
-
};
|
|
4670
|
-
this.pluginRegistrations.set(pluginName, reg);
|
|
4671
|
-
return reg;
|
|
4672
|
-
}
|
|
4673
|
-
/**
|
|
4674
|
-
* Create a PluginAPI instance that plugins use to register their capabilities.
|
|
4675
|
-
*/
|
|
4676
|
-
createPluginAPI(options = {}) {
|
|
4677
|
-
const toolRegistry = options.toolRegistry;
|
|
4678
|
-
const logger2 = options.logger ?? {
|
|
4679
|
-
debug: (...arguments_) => {
|
|
4680
|
-
console.debug("[plugin]", ...arguments_);
|
|
4681
|
-
},
|
|
4682
|
-
info: (...arguments_) => {
|
|
4683
|
-
console.info("[plugin]", ...arguments_);
|
|
4684
|
-
},
|
|
4685
|
-
warn: (...arguments_) => {
|
|
4686
|
-
console.warn("[plugin]", ...arguments_);
|
|
4687
|
-
},
|
|
4688
|
-
error: (...arguments_) => {
|
|
4689
|
-
console.error("[plugin]", ...arguments_);
|
|
4690
|
-
}
|
|
4691
|
-
};
|
|
4692
|
-
return {
|
|
4693
|
-
logger: logger2,
|
|
4694
|
-
registerTool(toolId, impl, schema) {
|
|
4695
|
-
toolRegistry?.registerTool(toolId, impl);
|
|
4696
|
-
if (schema) {
|
|
4697
|
-
registerToolParameterSchema(toolId, schema, {
|
|
4698
|
-
displayName: toolId,
|
|
4699
|
-
description: `Plugin tool: ${toolId}`
|
|
4700
|
-
});
|
|
4701
|
-
}
|
|
4702
|
-
},
|
|
4703
|
-
registerHook(type, handler, name) {
|
|
4704
|
-
const hookName = name ?? `plugin-hook:${type}:${Date.now().toString(36)}`;
|
|
4705
|
-
registerHook(type, handler, hookName);
|
|
4706
|
-
}
|
|
4707
|
-
};
|
|
4708
|
-
}
|
|
4709
|
-
registerPluginTools(pluginName, tools) {
|
|
4710
|
-
const reg = this.ensureRegistration(pluginName);
|
|
4711
|
-
for (const [toolId, , schema] of tools) {
|
|
4712
|
-
if (schema) {
|
|
4713
|
-
registerToolParameterSchema(toolId, schema, {
|
|
4714
|
-
displayName: toolId,
|
|
4715
|
-
description: `Plugin tool: ${toolId}`
|
|
4716
|
-
});
|
|
4717
|
-
}
|
|
4718
|
-
reg.tools.push(toolId);
|
|
4719
|
-
}
|
|
4720
|
-
}
|
|
4721
|
-
registerPluginHooks(pluginName, hooks) {
|
|
4722
|
-
const reg = this.ensureRegistration(pluginName);
|
|
4723
|
-
for (const [type, handler, name] of hooks) {
|
|
4724
|
-
const hookName = name ?? `plugin-hook:${pluginName}:${type}:${reg.hooks.length}`;
|
|
4725
|
-
registerHook(type, handler, hookName);
|
|
4726
|
-
reg.hooks.push({ type, name: hookName });
|
|
4727
|
-
}
|
|
4728
|
-
}
|
|
4729
|
-
getPluginRegistrations(pluginName) {
|
|
4730
|
-
return this.pluginRegistrations.get(pluginName);
|
|
4731
|
-
}
|
|
4732
|
-
clearPluginRegistrations() {
|
|
4733
|
-
this.pluginRegistrations.clear();
|
|
4734
|
-
}
|
|
4735
|
-
};
|
|
4736
|
-
var defaultPluginRegistryManager = new PluginRegistryManager();
|
|
4737
|
-
function createPluginAPI(options = {}) {
|
|
4738
|
-
return defaultPluginRegistryManager.createPluginAPI(options);
|
|
4739
|
-
}
|
|
4740
|
-
function registerPluginTools(pluginName, tools) {
|
|
4741
|
-
defaultPluginRegistryManager.registerPluginTools(pluginName, tools);
|
|
4742
|
-
}
|
|
4743
|
-
function registerPluginHooks(pluginName, hooks) {
|
|
4744
|
-
defaultPluginRegistryManager.registerPluginHooks(pluginName, hooks);
|
|
4745
|
-
}
|
|
4746
|
-
function getPluginRegistrations(pluginName) {
|
|
4747
|
-
return defaultPluginRegistryManager.getPluginRegistrations(pluginName);
|
|
4748
|
-
}
|
|
4749
|
-
function clearPluginRegistrations() {
|
|
4750
|
-
defaultPluginRegistryManager.clearPluginRegistrations();
|
|
4751
|
-
}
|
|
4752
|
-
|
|
4753
|
-
export {
|
|
4754
|
-
BUILTIN_AGENT_AGENT_LOOP_QUALITY_GATE_SCRIPT_ID,
|
|
4755
|
-
builtinAgentAgentLoopSources,
|
|
4756
|
-
getBuiltinAgentAgentLoopSource,
|
|
4757
|
-
loadAgentAgentLoopScript,
|
|
4758
|
-
createAgentAgentLoopDefinition,
|
|
4759
|
-
getAgentAgentLoopId,
|
|
4760
|
-
BUILTIN_AGENT_TOOL_LOOP_DEFAULT_SCRIPT_ID,
|
|
4761
|
-
builtinAgentToolLoopSources,
|
|
4762
|
-
getBuiltinAgentToolLoopSource,
|
|
4763
|
-
compactMessages,
|
|
4764
|
-
shouldCompact,
|
|
4765
|
-
autoCompact,
|
|
4766
|
-
loadAgentToolLoopScript,
|
|
4767
|
-
pluginRegistry,
|
|
4768
|
-
getActivePluginRegistry,
|
|
4769
|
-
runWithPluginRegistry,
|
|
4770
|
-
createAgentFrameworkHooks,
|
|
4771
|
-
runProcessPromptsHooks,
|
|
4772
|
-
runResponseCompleteHooks,
|
|
4773
|
-
runPostProcessHooks,
|
|
4774
|
-
runToolExecutedHooks,
|
|
4775
|
-
resolvePromptPluginMap,
|
|
4776
|
-
createHooksWithPlugins,
|
|
4777
|
-
matchToolCalling,
|
|
4778
|
-
matchAllToolCallings,
|
|
4779
|
-
nextLamportClockForConversation,
|
|
4780
|
-
HookRegistry,
|
|
4781
|
-
getDefaultHookRegistry,
|
|
4782
|
-
registerHook,
|
|
4783
|
-
unregisterHook,
|
|
4784
|
-
executeHooks,
|
|
4785
|
-
hasHooks,
|
|
4786
|
-
clearHooks,
|
|
4787
|
-
listRegisteredHookTypes,
|
|
4788
|
-
getHookCount,
|
|
4789
|
-
findPromptById,
|
|
4790
|
-
flattenPrompts,
|
|
4791
|
-
promptConcatStream,
|
|
4792
|
-
MEMELOOP_STRUCTURED_TOOL_KEY,
|
|
4793
|
-
truncateToolSummary,
|
|
4794
|
-
extractMemeloopStructuredToolPayload,
|
|
4795
|
-
matchPattern,
|
|
4796
|
-
mergePermissionSets,
|
|
4797
|
-
checkPermission,
|
|
4798
|
-
loadUserPermissions,
|
|
4799
|
-
saveUserPermissions,
|
|
4800
|
-
PERMISSIONS_TABLE_DDL,
|
|
4801
|
-
onApprovalRequest,
|
|
4802
|
-
evaluateApproval,
|
|
4803
|
-
requestApproval,
|
|
4804
|
-
resolveApproval,
|
|
4805
|
-
getPendingApprovals,
|
|
4806
|
-
cancelPendingApprovals,
|
|
4807
|
-
createAgentToolLoopRunner,
|
|
4808
|
-
createAgentToolLoopDefinition,
|
|
4809
|
-
resolveAgentToolLoopTerminalState,
|
|
4810
|
-
runAgentToolLoopTurn,
|
|
4811
|
-
getLoopRegistry,
|
|
4812
|
-
resetLoopRegistry,
|
|
4813
|
-
AGENT_TOOL_LOOP_ID,
|
|
4814
|
-
AGENT_AGENT_LOOP_ID,
|
|
4815
|
-
registerBuiltinLoops,
|
|
4816
|
-
registerToolParameterSchema,
|
|
4817
|
-
getToolParameterSchema,
|
|
4818
|
-
getToolMetadata,
|
|
4819
|
-
resolveQuestionAnswer,
|
|
4820
|
-
askQuestionConfigSchema,
|
|
4821
|
-
ASK_QUESTION_TOOL_ID,
|
|
4822
|
-
askQuestionImpl,
|
|
4823
|
-
askUserQuestionConfigSchema,
|
|
4824
|
-
ASK_USER_QUESTION_TOOL_ID,
|
|
4825
|
-
askUserQuestionImpl,
|
|
4826
|
-
mcpClientConfigSchema,
|
|
4827
|
-
mcpClientImpl,
|
|
4828
|
-
getMcpClientToolId,
|
|
4829
|
-
mcpForwardConfigSchema,
|
|
4830
|
-
mcpForwardImpl,
|
|
4831
|
-
getMcpForwardToolId,
|
|
4832
|
-
remoteAgentConfigSchema,
|
|
4833
|
-
remoteAgentListImpl,
|
|
4834
|
-
remoteAgentImpl,
|
|
4835
|
-
getRemoteAgentToolId,
|
|
4836
|
-
spawnAgentConfigSchema,
|
|
4837
|
-
spawnAgentImpl,
|
|
4838
|
-
getSpawnAgentToolId,
|
|
4839
|
-
buildAgent,
|
|
4840
|
-
planAgent,
|
|
4841
|
-
exploreAgent,
|
|
4842
|
-
oracleAgent,
|
|
4843
|
-
librarianAgent,
|
|
4844
|
-
BUILTIN_AGENT_PROFILES,
|
|
4845
|
-
AgentProfileRegistry,
|
|
4846
|
-
getAgentProfileRegistry,
|
|
4847
|
-
resetAgentProfileRegistry,
|
|
4848
|
-
taskToolConfigSchema,
|
|
4849
|
-
taskToolImpl,
|
|
4850
|
-
getTaskToolId,
|
|
4851
|
-
todoWriteConfigSchema,
|
|
4852
|
-
TODO_WRITE_TOOL_ID,
|
|
4853
|
-
__clearTodoStore,
|
|
4854
|
-
todoWriteImpl,
|
|
4855
|
-
PLUGIN_MCP_CLIENT,
|
|
4856
|
-
PLUGIN_MCP_FORWARD,
|
|
4857
|
-
PLUGIN_SPAWN_AGENT,
|
|
4858
|
-
PLUGIN_REMOTE_AGENT,
|
|
4859
|
-
PLUGIN_ASK_QUESTION,
|
|
4860
|
-
PLUGIN_TODO_WRITE,
|
|
4861
|
-
PLUGIN_ASK_USER_QUESTION,
|
|
4862
|
-
PLUGIN_TASK,
|
|
4863
|
-
BUILTIN_TOOL_PLUGIN_IDS,
|
|
4864
|
-
getBuiltinToolPlugins,
|
|
4865
|
-
registerBuiltinToolPlugins,
|
|
4866
|
-
TokenTracker,
|
|
4867
|
-
getBuiltinLoopProfiles,
|
|
4868
|
-
getBuiltinLoopProfile,
|
|
4869
|
-
registerBuiltinLoopProfiles,
|
|
4870
|
-
registerBuiltinPromptPlugins,
|
|
4871
|
-
createAgentLoopRunner,
|
|
4872
|
-
createMemeLoopRuntime,
|
|
4873
|
-
getCategoryConfig,
|
|
4874
|
-
isTaskCategory,
|
|
4875
|
-
ALL_CATEGORIES,
|
|
4876
|
-
resolveCategory,
|
|
4877
|
-
ProviderRegistry,
|
|
4878
|
-
createChatMessage,
|
|
4879
|
-
createAgentInstanceFromDefinition,
|
|
4880
|
-
validatePluginManifest,
|
|
4881
|
-
loadPluginModule,
|
|
4882
|
-
loadPluginModules,
|
|
4883
|
-
unloadPlugin,
|
|
4884
|
-
listPlugins,
|
|
4885
|
-
getLoadedPlugin,
|
|
4886
|
-
isPluginLoaded,
|
|
4887
|
-
unloadAllPlugins,
|
|
4888
|
-
createPluginAPI,
|
|
4889
|
-
registerPluginTools,
|
|
4890
|
-
registerPluginHooks,
|
|
4891
|
-
getPluginRegistrations,
|
|
4892
|
-
clearPluginRegistrations
|
|
4893
|
-
};
|
|
4894
|
-
//# sourceMappingURL=chunk-3CN7DFBL.js.map
|