@meetopenbot/openbot 0.2.6 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto-model.js +13 -0
- package/dist/build-tools.js +26 -0
- package/dist/history.js +81 -5
- package/dist/index.js +66 -67
- package/dist/model.js +29 -17
- package/dist/output-buffer.js +31 -0
- package/dist/runtime.js +360 -465
- package/dist/stream-error.js +6 -0
- package/dist/system-prompt.js +4 -0
- package/dist/tools/approval.js +137 -108
- package/dist/tools/ask-agent.js +39 -47
- package/dist/tools/memory.js +41 -73
- package/dist/tools/start-work.js +61 -141
- package/dist/tools/storage.js +62 -424
- package/dist/tools/thread-status.js +22 -38
- package/dist/tools/thread-title.js +57 -0
- package/dist/tools/todo.js +33 -52
- package/dist/types.js +0 -8
- package/package.json +8 -6
- package/dist/tools/bash.js +0 -432
- package/dist/tools/preview.js +0 -269
- package/dist/tools/ui.js +0 -120
- package/dist/utils/workspace-url.js +0 -6
package/dist/system-prompt.js
CHANGED
|
@@ -28,6 +28,10 @@ export const OPENBOT_SYSTEM_PROMPT = [
|
|
|
28
28
|
"- The current list is injected into context each turn as `## TODOS`; call `todo_read` only if you need an explicit refresh.",
|
|
29
29
|
"- Do not stop with open `pending` or `in_progress` items unless you are blocked and have told the user why.",
|
|
30
30
|
"",
|
|
31
|
+
"# JOB TITLE",
|
|
32
|
+
"- Unnamed jobs get a system hint to call `set_thread_title` once with a concise, human-readable title.",
|
|
33
|
+
"- Call `set_thread_title` again only if the topic clearly changes.",
|
|
34
|
+
"",
|
|
31
35
|
"# JOB STATUS",
|
|
32
36
|
"- Every thread is a job. Status is `working` (in progress), `needs_input`, `ready_for_review`, `completed`, or `archived`.",
|
|
33
37
|
"- Status is the job's workflow state, not whether an agent is currently executing.",
|
package/dist/tools/approval.js
CHANGED
|
@@ -1,130 +1,159 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { agentOutput } from '@meetopenbot/plugin-sdk';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
+
* Gates protected tool calls behind a UI confirmation widget.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* Wraps `execute` for the listed actions: emit an approval widget, wait for
|
|
7
|
+
* the click via `host.awaitWidgetResponse`, then run the inner execute or
|
|
8
|
+
* return a denial result.
|
|
7
9
|
*/
|
|
8
|
-
// In-memory tracking for pending approval IDs with TTL (shared across plugin instances)
|
|
9
10
|
const pendingApprovals = new Map();
|
|
10
11
|
const TTL_MS = 4 * 60 * 60 * 1000; // 4 hours
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
function actionToolName(action) {
|
|
13
|
+
return action.startsWith('action:') ? action.slice('action:'.length) : action;
|
|
14
|
+
}
|
|
15
|
+
function normalizeResult(result) {
|
|
16
|
+
return typeof result === 'string' ? { output: result } : result;
|
|
17
|
+
}
|
|
18
|
+
async function emitApprovalWidget(execCtx, args) {
|
|
19
|
+
const threadId = execCtx.threadId ?? execCtx.state.threadId;
|
|
20
|
+
await execCtx.emit({
|
|
21
|
+
type: 'client:ui:widget',
|
|
22
|
+
data: {
|
|
23
|
+
widgetId: args.widgetId,
|
|
24
|
+
kind: 'message',
|
|
25
|
+
title: args.title,
|
|
26
|
+
body: args.body,
|
|
27
|
+
...(args.state ? { state: args.state } : {}),
|
|
28
|
+
...(args.state && args.state !== 'open'
|
|
29
|
+
? { display: 'collapsed', disabled: true, actions: [] }
|
|
30
|
+
: { actions: args.actions }),
|
|
31
|
+
metadata: {
|
|
32
|
+
type: 'approval:request',
|
|
33
|
+
originalEvent: {
|
|
34
|
+
type: args.action,
|
|
35
|
+
data: args.toolArgs,
|
|
36
|
+
meta: {
|
|
37
|
+
toolCallId: execCtx.toolCallId,
|
|
38
|
+
agentId: execCtx.agentId,
|
|
39
|
+
threadId,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
meta: { agentId: execCtx.agentId, threadId },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Wrap `execute` on tools whose names appear in `actionsToApprove`
|
|
49
|
+
* (bare name or `action:<name>`).
|
|
50
|
+
*/
|
|
51
|
+
export function wrapToolsWithApproval(tools, actionsToApprove, ctx) {
|
|
52
|
+
if (!actionsToApprove.length)
|
|
53
|
+
return tools;
|
|
54
|
+
const namesToWrap = new Set(actionsToApprove.map(actionToolName));
|
|
55
|
+
const wrapped = { ...tools };
|
|
56
|
+
for (const [name, definition] of Object.entries(tools)) {
|
|
57
|
+
if (!namesToWrap.has(name) || !definition.execute)
|
|
58
|
+
continue;
|
|
59
|
+
const inner = definition.execute;
|
|
60
|
+
const action = `action:${name}`;
|
|
61
|
+
wrapped[name] = {
|
|
62
|
+
...definition,
|
|
63
|
+
async execute(args, execCtx) {
|
|
64
|
+
const displayData = JSON.stringify(args ?? '') || '';
|
|
25
65
|
const widgetId = randomUUID();
|
|
26
66
|
pendingApprovals.set(widgetId, Date.now());
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
67
|
+
await emitApprovalWidget(execCtx, {
|
|
68
|
+
widgetId,
|
|
69
|
+
action,
|
|
70
|
+
toolArgs: args,
|
|
71
|
+
title: `The agent wants to perform \`${action}\``,
|
|
72
|
+
body: displayData,
|
|
73
|
+
actions: [
|
|
74
|
+
{ id: 'approve', label: 'Approve', variant: 'primary' },
|
|
75
|
+
{ id: 'deny', label: 'Deny', variant: 'danger' },
|
|
76
|
+
],
|
|
77
|
+
});
|
|
78
|
+
let response;
|
|
79
|
+
try {
|
|
80
|
+
response = await execCtx.host.awaitWidgetResponse(widgetId, execCtx.abortSignal);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
pendingApprovals.delete(widgetId);
|
|
84
|
+
const message = error instanceof Error ? error.message : 'Approval interrupted';
|
|
85
|
+
await emitApprovalWidget(execCtx, {
|
|
30
86
|
widgetId,
|
|
31
|
-
|
|
32
|
-
|
|
87
|
+
action,
|
|
88
|
+
toolArgs: args,
|
|
89
|
+
title: 'Action Denied',
|
|
33
90
|
body: displayData,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
{ id: 'approve', label: 'Approve', variant: 'primary' },
|
|
40
|
-
{ id: 'deny', label: 'Deny', variant: 'danger' },
|
|
41
|
-
],
|
|
42
|
-
},
|
|
43
|
-
meta: { agentId: context.state.agentId, threadId: context.state.threadId },
|
|
44
|
-
};
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
// Handle the user's response from the UI widget
|
|
48
|
-
builder.on('client:ui:widget:response', async function* (event, context) {
|
|
49
|
-
const { widgetId, actionId } = event.data;
|
|
50
|
-
const metadata = event.data?.metadata;
|
|
51
|
-
if (metadata?.type !== 'approval:request')
|
|
52
|
-
return;
|
|
53
|
-
// Verify the widget is still pending and hasn't expired
|
|
54
|
-
if (!widgetId || !pendingApprovals.has(widgetId)) {
|
|
55
|
-
console.warn(`[approval] Received response for unknown or already handled widget: ${widgetId}`);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
const timestamp = pendingApprovals.get(widgetId);
|
|
59
|
-
if (Date.now() - timestamp > TTL_MS) {
|
|
91
|
+
state: 'cancelled',
|
|
92
|
+
});
|
|
93
|
+
return { success: false, error: message, output: message };
|
|
94
|
+
}
|
|
95
|
+
const timestamp = pendingApprovals.get(widgetId);
|
|
60
96
|
pendingApprovals.delete(widgetId);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
97
|
+
if (timestamp == null) {
|
|
98
|
+
console.warn(`[approval] Received response for unknown or already handled widget: ${widgetId}`);
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
error: 'Approval request is no longer pending.',
|
|
102
|
+
output: 'Approval request is no longer pending.',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (Date.now() - timestamp > TTL_MS) {
|
|
106
|
+
console.warn(`[approval] Received response for expired widget: ${widgetId}`);
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
error: 'Approval request expired.',
|
|
110
|
+
output: 'Approval request expired.',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const approved = response.actionId === 'approve';
|
|
114
|
+
await emitApprovalWidget(execCtx, {
|
|
73
115
|
widgetId,
|
|
74
|
-
|
|
116
|
+
action,
|
|
117
|
+
toolArgs: args,
|
|
75
118
|
title: `Action ${approved ? 'Approved' : 'Denied'}`,
|
|
76
119
|
body: displayData,
|
|
77
120
|
state: approved ? 'submitted' : 'cancelled',
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
yield {
|
|
87
|
-
...originalEvent,
|
|
121
|
+
});
|
|
122
|
+
if (approved) {
|
|
123
|
+
return normalizeResult(await inner(args, execCtx));
|
|
124
|
+
}
|
|
125
|
+
const threadId = execCtx.threadId ?? execCtx.state.threadId;
|
|
126
|
+
const originalEvent = {
|
|
127
|
+
type: action,
|
|
128
|
+
data: args,
|
|
88
129
|
meta: {
|
|
89
|
-
|
|
90
|
-
|
|
130
|
+
toolCallId: execCtx.toolCallId,
|
|
131
|
+
agentId: execCtx.agentId,
|
|
132
|
+
threadId,
|
|
133
|
+
approvalStatus: 'denied',
|
|
91
134
|
},
|
|
92
135
|
};
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// Manually store the original event with denied status so it's recorded in history
|
|
96
|
-
// but NOT re-emitted to the pipeline (to avoid actual execution).
|
|
97
|
-
if (storage) {
|
|
136
|
+
const storage = ctx.storage;
|
|
137
|
+
if (storage.storeEvent) {
|
|
98
138
|
await storage.storeEvent({
|
|
99
|
-
channelId:
|
|
100
|
-
threadId
|
|
101
|
-
event:
|
|
102
|
-
...originalEvent,
|
|
103
|
-
meta: {
|
|
104
|
-
...(originalEvent.meta || {}),
|
|
105
|
-
approvalStatus: 'denied',
|
|
106
|
-
},
|
|
107
|
-
},
|
|
139
|
+
channelId: execCtx.channelId ?? execCtx.state.channelId ?? '',
|
|
140
|
+
threadId,
|
|
141
|
+
event: originalEvent,
|
|
108
142
|
});
|
|
109
143
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
};
|
|
121
|
-
yield {
|
|
122
|
-
type: 'agent:output',
|
|
123
|
-
data: { content: `Action \`${originalEvent.type}\` was denied.` },
|
|
124
|
-
meta: { agentId: context.state.agentId },
|
|
144
|
+
await execCtx.emit(agentOutput({
|
|
145
|
+
agentId: execCtx.agentId,
|
|
146
|
+
threadId,
|
|
147
|
+
content: `Action \`${action}\` was denied.`,
|
|
148
|
+
}));
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
error: 'Action denied by user.',
|
|
152
|
+
stderr: 'Action denied by user.',
|
|
153
|
+
output: 'Action denied by user.',
|
|
125
154
|
};
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return wrapped;
|
|
159
|
+
}
|
package/dist/tools/ask-agent.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* `ask_agent` — OpenBot sends a message to another agent in its own harness.
|
|
5
|
+
* Only the orchestrator may ask. Child events stream into this thread so the
|
|
6
|
+
* human sees the specialist speak; the child's last message is the tool result.
|
|
7
|
+
*/
|
|
8
|
+
export const toolDefinitions = {
|
|
4
9
|
ask_agent: {
|
|
5
10
|
description: "Ask another installed agent to do work in their own harness. Write the prompt as a message to a colleague, not as a command to a tool. The human sees their reply in this thread; do not restate it when you close.",
|
|
6
11
|
inputSchema: z.object({
|
|
@@ -11,29 +16,30 @@ const askAgentToolDefinitions = {
|
|
|
11
16
|
}),
|
|
12
17
|
},
|
|
13
18
|
};
|
|
14
|
-
async function
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
async function runAskedAgent(ctx, args) {
|
|
20
|
+
if (ctx.agentId !== ctx.host.orchestratorAgentId) {
|
|
21
|
+
return {
|
|
22
|
+
success: false,
|
|
23
|
+
error: "Only OpenBot can ask other agents.",
|
|
24
|
+
output: "Only OpenBot can ask other agents.",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const agentId = args.agentId;
|
|
28
|
+
const prompt = args.prompt;
|
|
29
|
+
const toolCallId = ctx.toolCallId;
|
|
30
|
+
if (!agentId || !prompt || !toolCallId) {
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
error: "agentId, prompt, and toolCallId are required",
|
|
34
|
+
output: "agentId, prompt, and toolCallId are required",
|
|
23
35
|
};
|
|
24
|
-
return;
|
|
25
36
|
}
|
|
26
|
-
const agentId = event.data.agentId;
|
|
27
|
-
const prompt = event.data.prompt;
|
|
28
|
-
const toolCallId = event.meta?.toolCallId;
|
|
29
|
-
if (!agentId || !prompt || !toolCallId)
|
|
30
|
-
return;
|
|
31
37
|
const runId = `ask_${randomUUID()}`;
|
|
32
38
|
let lastAgentOutput = "";
|
|
33
39
|
const eventQueue = [];
|
|
34
40
|
let resolveNext = null;
|
|
35
41
|
let isFinished = false;
|
|
36
|
-
const runPromise =
|
|
42
|
+
const runPromise = ctx.host
|
|
37
43
|
.runAgent({
|
|
38
44
|
runId,
|
|
39
45
|
agentId,
|
|
@@ -45,20 +51,20 @@ async function* runAskedAgent(pluginContext, event, context, resultType) {
|
|
|
45
51
|
agentId,
|
|
46
52
|
},
|
|
47
53
|
meta: {
|
|
48
|
-
channelId:
|
|
49
|
-
threadId:
|
|
50
|
-
parentAgentId:
|
|
54
|
+
channelId: ctx.channelId ?? ctx.state.channelId ?? '',
|
|
55
|
+
threadId: ctx.threadId ?? ctx.state.threadId,
|
|
56
|
+
parentAgentId: ctx.agentId,
|
|
51
57
|
parentToolCallId: toolCallId,
|
|
52
58
|
},
|
|
53
59
|
},
|
|
54
|
-
publicBaseUrl:
|
|
60
|
+
publicBaseUrl: ctx.publicBaseUrl,
|
|
55
61
|
persistEvents: false,
|
|
56
62
|
onEvent: async (outEvent) => {
|
|
57
63
|
const enrichedEvent = {
|
|
58
64
|
...outEvent,
|
|
59
65
|
meta: {
|
|
60
66
|
...outEvent.meta,
|
|
61
|
-
parentAgentId:
|
|
67
|
+
parentAgentId: ctx.agentId,
|
|
62
68
|
parentToolCallId: toolCallId,
|
|
63
69
|
},
|
|
64
70
|
};
|
|
@@ -89,35 +95,21 @@ async function* runAskedAgent(pluginContext, event, context, resultType) {
|
|
|
89
95
|
});
|
|
90
96
|
}
|
|
91
97
|
while (eventQueue.length > 0) {
|
|
92
|
-
|
|
98
|
+
await ctx.emit(eventQueue.shift());
|
|
93
99
|
}
|
|
94
100
|
}
|
|
95
101
|
await runPromise;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
success: true,
|
|
100
|
-
output: lastAgentOutput,
|
|
101
|
-
},
|
|
102
|
-
meta: {
|
|
103
|
-
...event.meta,
|
|
104
|
-
agentId: context.state.agentId,
|
|
105
|
-
toolCallId,
|
|
106
|
-
},
|
|
102
|
+
return {
|
|
103
|
+
success: true,
|
|
104
|
+
output: lastAgentOutput,
|
|
107
105
|
};
|
|
108
106
|
}
|
|
109
|
-
export const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
builder.on("action:ask_agent", async function* (event, context) {
|
|
116
|
-
yield* runAskedAgent(pluginContext, event, context, "action:ask_agent:result");
|
|
117
|
-
});
|
|
118
|
-
builder.on("action:delegate_task", async function* (event, context) {
|
|
119
|
-
yield* runAskedAgent(pluginContext, event, context, "action:delegate_task:result");
|
|
120
|
-
});
|
|
107
|
+
export const tools = {
|
|
108
|
+
ask_agent: {
|
|
109
|
+
...toolDefinitions.ask_agent,
|
|
110
|
+
execute: async (rawArgs, ctx) => {
|
|
111
|
+
return runAskedAgent(ctx, (rawArgs ?? {}));
|
|
112
|
+
},
|
|
121
113
|
},
|
|
122
114
|
};
|
|
123
|
-
export
|
|
115
|
+
export const askAgentTools = tools;
|
package/dist/tools/memory.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import z from 'zod';
|
|
2
|
-
import { asActionBuilder } from '../types.js';
|
|
3
2
|
/**
|
|
4
3
|
* Resolve a scope alias to a concrete scope string. Aliases let tools accept
|
|
5
|
-
* `agent`/`channel`/`global` without knowing the active ids;
|
|
6
|
-
*
|
|
4
|
+
* `agent`/`channel`/`global` without knowing the active ids; they are rewritten
|
|
5
|
+
* using execute context state.
|
|
7
6
|
*/
|
|
8
7
|
function resolveMemoryScope(alias, state) {
|
|
9
8
|
switch (alias) {
|
|
@@ -24,11 +23,11 @@ function resolveMemoryScopeFilter(alias, state) {
|
|
|
24
23
|
}
|
|
25
24
|
return [resolveMemoryScope(alias, state)];
|
|
26
25
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
26
|
+
function fail(error) {
|
|
27
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
28
|
+
return { success: false, error: message, output: message };
|
|
29
|
+
}
|
|
30
|
+
export const toolDefinitions = {
|
|
32
31
|
remember: {
|
|
33
32
|
description: 'Persist a durable fact, preference, or note to long-term memory so it can be recalled in future turns and runs. Use for stable information (user preferences, project conventions, contact details, decisions); avoid using it for transient chatter or per-step scratch state — that belongs in thread state. Keep entries short and self-contained.',
|
|
34
33
|
inputSchema: z.object({
|
|
@@ -74,90 +73,59 @@ const memoryToolDefinitions = {
|
|
|
74
73
|
}),
|
|
75
74
|
},
|
|
76
75
|
};
|
|
77
|
-
export const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
toolDefinitions: memoryToolDefinitions,
|
|
82
|
-
factory: ({ storage }) => (builder) => {
|
|
83
|
-
const store = storage;
|
|
84
|
-
const actions = asActionBuilder(builder);
|
|
85
|
-
actions.on('remember', async function* (event, context) {
|
|
86
|
-
const resultMeta = { ...(event.meta || {}), agentId: context.state.agentId };
|
|
76
|
+
export const tools = {
|
|
77
|
+
remember: {
|
|
78
|
+
...toolDefinitions.remember,
|
|
79
|
+
execute: async (rawArgs, ctx) => {
|
|
87
80
|
try {
|
|
88
|
-
const { content, scope, tags } =
|
|
89
|
-
const record = await
|
|
90
|
-
scope: resolveMemoryScope(scope,
|
|
81
|
+
const { content, scope, tags } = (rawArgs ?? {});
|
|
82
|
+
const record = await ctx.storage.appendMemory({
|
|
83
|
+
scope: resolveMemoryScope(scope, ctx.state),
|
|
91
84
|
content,
|
|
92
85
|
tags,
|
|
93
86
|
});
|
|
94
|
-
|
|
95
|
-
type: 'action:remember:result',
|
|
96
|
-
data: { success: true, record },
|
|
97
|
-
meta: resultMeta,
|
|
98
|
-
};
|
|
87
|
+
return { success: true, record, output: JSON.stringify(record) };
|
|
99
88
|
}
|
|
100
89
|
catch (error) {
|
|
101
|
-
|
|
102
|
-
type: 'action:remember:result',
|
|
103
|
-
data: {
|
|
104
|
-
success: false,
|
|
105
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
106
|
-
},
|
|
107
|
-
meta: resultMeta,
|
|
108
|
-
};
|
|
90
|
+
return fail(error);
|
|
109
91
|
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
recall: {
|
|
95
|
+
...toolDefinitions.recall,
|
|
96
|
+
execute: async (rawArgs, ctx) => {
|
|
113
97
|
try {
|
|
114
|
-
const { query, tag, scope, limit } =
|
|
115
|
-
const records = await
|
|
116
|
-
scopes: resolveMemoryScopeFilter(scope,
|
|
98
|
+
const { query, tag, scope, limit } = (rawArgs ?? {});
|
|
99
|
+
const records = await ctx.storage.listMemories({
|
|
100
|
+
scopes: resolveMemoryScopeFilter(scope, ctx.state),
|
|
117
101
|
query,
|
|
118
102
|
tag,
|
|
119
103
|
limit,
|
|
120
104
|
});
|
|
121
|
-
|
|
122
|
-
type: 'action:recall:result',
|
|
123
|
-
data: { success: true, records },
|
|
124
|
-
meta: resultMeta,
|
|
125
|
-
};
|
|
105
|
+
return { success: true, records, output: JSON.stringify(records) };
|
|
126
106
|
}
|
|
127
107
|
catch (error) {
|
|
128
|
-
|
|
129
|
-
type: 'action:recall:result',
|
|
130
|
-
data: {
|
|
131
|
-
success: false,
|
|
132
|
-
records: [],
|
|
133
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
134
|
-
},
|
|
135
|
-
meta: resultMeta,
|
|
136
|
-
};
|
|
108
|
+
return { ...fail(error), records: [] };
|
|
137
109
|
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
forget: {
|
|
113
|
+
...toolDefinitions.forget,
|
|
114
|
+
execute: async (rawArgs, ctx) => {
|
|
141
115
|
try {
|
|
142
|
-
const deleted = await
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
116
|
+
const deleted = await ctx.storage.deleteMemory({
|
|
117
|
+
id: (rawArgs ?? {}).id,
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
success: true,
|
|
121
|
+
deleted,
|
|
122
|
+
output: deleted ? 'Memory deleted.' : 'Memory not found.',
|
|
147
123
|
};
|
|
148
124
|
}
|
|
149
125
|
catch (error) {
|
|
150
|
-
|
|
151
|
-
type: 'action:forget:result',
|
|
152
|
-
data: {
|
|
153
|
-
success: false,
|
|
154
|
-
deleted: false,
|
|
155
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
156
|
-
},
|
|
157
|
-
meta: resultMeta,
|
|
158
|
-
};
|
|
126
|
+
return { ...fail(error), deleted: false };
|
|
159
127
|
}
|
|
160
|
-
}
|
|
128
|
+
},
|
|
161
129
|
},
|
|
162
130
|
};
|
|
163
|
-
export
|
|
131
|
+
export const memoryTools = tools;
|