@grovina/brian 0.1.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/README.md +18 -0
- package/dist/agent.js +217 -0
- package/dist/agent.js.map +1 -0
- package/dist/channels/slack.js +110 -0
- package/dist/channels/slack.js.map +1 -0
- package/dist/channels/telegram.js +73 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/cli/self.js +63 -0
- package/dist/cli/self.js.map +1 -0
- package/dist/client.js +68 -0
- package/dist/client.js.map +1 -0
- package/dist/env.js +16 -0
- package/dist/env.js.map +1 -0
- package/dist/logs.js +32 -0
- package/dist/logs.js.map +1 -0
- package/dist/model.js +203 -0
- package/dist/model.js.map +1 -0
- package/dist/prompt.js +147 -0
- package/dist/prompt.js.map +1 -0
- package/dist/start.js +147 -0
- package/dist/start.js.map +1 -0
- package/dist/tools/bash.js +111 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/communication.js +132 -0
- package/dist/tools/communication.js.map +1 -0
- package/dist/tools/mind.js +67 -0
- package/dist/tools/mind.js.map +1 -0
- package/dist/tools/process.js +57 -0
- package/dist/tools/process.js.map +1 -0
- package/dist/tools/terminal.js +113 -0
- package/dist/tools/terminal.js.map +1 -0
- package/dist/tools/wait.js +48 -0
- package/dist/tools/wait.js.map +1 -0
- package/dist/turns.js +102 -0
- package/dist/turns.js.map +1 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/dist/updates.js +65 -0
- package/dist/updates.js.map +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export function communicationTools(router) {
|
|
2
|
+
return [
|
|
3
|
+
{
|
|
4
|
+
name: 'channel_send',
|
|
5
|
+
definition: {
|
|
6
|
+
name: 'channel_send',
|
|
7
|
+
description: 'Send a message to a communication channel. Reply to an inbound event or post to a conversation directly.',
|
|
8
|
+
parameters: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
mode: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
enum: ['reply', 'conversation'],
|
|
14
|
+
description: 'Use reply to continue an inbound thread, or conversation for direct posting',
|
|
15
|
+
},
|
|
16
|
+
text: { type: 'string', description: 'Message text to send' },
|
|
17
|
+
source_event_id: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'Inbound event id when mode=reply',
|
|
20
|
+
},
|
|
21
|
+
channel: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
enum: ['slack', 'telegram'],
|
|
24
|
+
description: 'Target channel when mode=conversation',
|
|
25
|
+
},
|
|
26
|
+
conversation_id: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Target conversation identifier when mode=conversation',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
required: ['mode', 'text'],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
async execute(input) {
|
|
35
|
+
const { mode, text, source_event_id, channel, conversation_id } = input;
|
|
36
|
+
if (mode === 'reply') {
|
|
37
|
+
if (!source_event_id) {
|
|
38
|
+
throw new Error('source_event_id is required when mode=reply');
|
|
39
|
+
}
|
|
40
|
+
const route = router.getByEventId(source_event_id);
|
|
41
|
+
if (!route) {
|
|
42
|
+
throw new Error(`Unknown source_event_id: ${source_event_id}`);
|
|
43
|
+
}
|
|
44
|
+
await route.adapter.sendReply(route.message, text);
|
|
45
|
+
return 'Sent';
|
|
46
|
+
}
|
|
47
|
+
if (!channel || !conversation_id) {
|
|
48
|
+
throw new Error('channel and conversation_id are required when mode=conversation');
|
|
49
|
+
}
|
|
50
|
+
const adapter = router.getAdapter(channel);
|
|
51
|
+
if (!adapter || !adapter.sendToConversation) {
|
|
52
|
+
throw new Error(`Unsupported conversation send for channel: ${channel}`);
|
|
53
|
+
}
|
|
54
|
+
await adapter.sendToConversation(conversation_id, text);
|
|
55
|
+
return 'Sent';
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'channel_react',
|
|
60
|
+
definition: {
|
|
61
|
+
name: 'channel_react',
|
|
62
|
+
description: 'Add a reaction to a message on a channel (Slack only).',
|
|
63
|
+
parameters: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
source_event_id: {
|
|
67
|
+
type: 'string',
|
|
68
|
+
description: 'Event id of the message to react to',
|
|
69
|
+
},
|
|
70
|
+
emoji: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
description: 'Reaction emoji name (without colons, e.g. "thumbsup")',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
required: ['source_event_id', 'emoji'],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
async execute(input) {
|
|
79
|
+
const { source_event_id, emoji } = input;
|
|
80
|
+
const route = router.getByEventId(source_event_id);
|
|
81
|
+
if (!route) {
|
|
82
|
+
throw new Error(`Unknown source_event_id: ${source_event_id}`);
|
|
83
|
+
}
|
|
84
|
+
if (!route.adapter.addReaction) {
|
|
85
|
+
throw new Error('Reactions are not supported on this channel');
|
|
86
|
+
}
|
|
87
|
+
await route.adapter.addReaction(route.message, emoji);
|
|
88
|
+
return 'Reacted';
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'channel_history',
|
|
93
|
+
definition: {
|
|
94
|
+
name: 'channel_history',
|
|
95
|
+
description: 'Read recent message history from a channel conversation. Useful for catching up on context.',
|
|
96
|
+
parameters: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
channel: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
enum: ['slack', 'telegram'],
|
|
102
|
+
description: 'Channel to read from',
|
|
103
|
+
},
|
|
104
|
+
conversation_id: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
description: 'Conversation identifier',
|
|
107
|
+
},
|
|
108
|
+
limit: {
|
|
109
|
+
type: 'number',
|
|
110
|
+
description: 'Max messages to return (default 20)',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
required: ['channel', 'conversation_id'],
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
async execute(input) {
|
|
117
|
+
const { channel, conversation_id, limit } = input;
|
|
118
|
+
const adapter = router.getAdapter(channel);
|
|
119
|
+
if (!adapter || !adapter.getHistory) {
|
|
120
|
+
throw new Error(`History reading not supported for channel: ${channel}`);
|
|
121
|
+
}
|
|
122
|
+
const messages = await adapter.getHistory(conversation_id, limit ?? 20);
|
|
123
|
+
if (messages.length === 0)
|
|
124
|
+
return 'No recent messages.';
|
|
125
|
+
return messages
|
|
126
|
+
.map((m) => `[${m.ts}] ${m.userId}: ${m.text}`)
|
|
127
|
+
.join('\n');
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=communication.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"communication.js","sourceRoot":"","sources":["../../src/tools/communication.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { workerApi } from '../client.js';
|
|
2
|
+
export const mindTools = [
|
|
3
|
+
{
|
|
4
|
+
name: 'mind_list',
|
|
5
|
+
definition: {
|
|
6
|
+
name: 'mind_list',
|
|
7
|
+
description: 'List all mind paths and their metadata.',
|
|
8
|
+
parameters: { type: 'object', properties: {} },
|
|
9
|
+
},
|
|
10
|
+
async execute() {
|
|
11
|
+
const entries = await workerApi.listMinds();
|
|
12
|
+
if (entries.length === 0)
|
|
13
|
+
return 'No mind entries.';
|
|
14
|
+
const lines = entries.map((e) => `- ${e.path} (updated by ${e.updatedBy} at ${e.updatedAt})`);
|
|
15
|
+
return lines.join('\n');
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'mind_read',
|
|
20
|
+
definition: {
|
|
21
|
+
name: 'mind_read',
|
|
22
|
+
description: 'Read the content of a mind path.',
|
|
23
|
+
parameters: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
path: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Mind path to read (e.g. identity, relationships, operations, learnings, journal, memory)',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
required: ['path'],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
async execute(input) {
|
|
35
|
+
const { path } = input;
|
|
36
|
+
const mind = await workerApi.getMind(path);
|
|
37
|
+
return mind.content || '(empty)';
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'mind_write',
|
|
42
|
+
definition: {
|
|
43
|
+
name: 'mind_write',
|
|
44
|
+
description: 'Write or update a mind path. Use this to persist durable knowledge, identity, relationships, operational rules, learnings, journal entries, or project context.',
|
|
45
|
+
parameters: {
|
|
46
|
+
type: 'object',
|
|
47
|
+
properties: {
|
|
48
|
+
path: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: 'Mind path to write (e.g. identity, relationships, operations, learnings, journal, memory)',
|
|
51
|
+
},
|
|
52
|
+
content: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
description: 'Full content to write (replaces existing content)',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
required: ['path', 'content'],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
async execute(input) {
|
|
61
|
+
const { path, content } = input;
|
|
62
|
+
await workerApi.putMind(path, content);
|
|
63
|
+
return `Mind path "${path}" updated.`;
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
//# sourceMappingURL=mind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mind.js","sourceRoot":"","sources":["../../src/tools/mind.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,MAAM,CAAC,MAAM,SAAS,GAAW;IAC/B;QACE,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE;YACV,IAAI,EAAE,WAAW;YACjB,WAAW,EAAE,yCAAyC;YACtD,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;SAC/C;QACD,KAAK,CAAC,OAAO;YACX,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,SAAS,EAAE,CAAC;YAC5C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,kBAAkB,CAAC;YACpD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CACvB,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,gBAAgB,CAAC,CAAC,SAAS,OAAO,CAAC,CAAC,SAAS,GAAG,CACnE,CAAC;YACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;KACF;IACD;QACE,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE;YACV,IAAI,EAAE,WAAW;YACjB,WAAW,EAAE,kCAAkC;YAC/C,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,IAAI,EAAE;wBACJ,IAAI,EAAE,QAAQ;wBACd,WAAW,EACT,0FAA0F;qBAC7F;iBACF;gBACD,QAAQ,EAAE,CAAC,MAAM,CAAC;aACnB;SACF;QACD,KAAK,CAAC,OAAO,CAAC,KAAK;YACjB,MAAM,EAAE,IAAI,EAAE,GAAG,KAAyB,CAAC;YAC3C,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC3C,OAAO,IAAI,CAAC,OAAO,IAAI,SAAS,CAAC;QACnC,CAAC;KACF;IACD;QACE,IAAI,EAAE,YAAY;QAClB,UAAU,EAAE;YACV,IAAI,EAAE,YAAY;YAClB,WAAW,EACT,iKAAiK;YACnK,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,IAAI,EAAE;wBACJ,IAAI,EAAE,QAAQ;wBACd,WAAW,EACT,2FAA2F;qBAC9F;oBACD,OAAO,EAAE;wBACP,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,mDAAmD;qBACjE;iBACF;gBACD,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;aAC9B;SACF;QACD,KAAK,CAAC,OAAO,CAAC,KAAK;YACjB,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,KAA0C,CAAC;YACrE,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACvC,OAAO,cAAc,IAAI,YAAY,CAAC;QACxC,CAAC;KACF;CACF,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const DEFAULT_DRAIN_MS = 100;
|
|
2
|
+
const FORCE_KILL_DELAY_MS = 5_000;
|
|
3
|
+
export function shellEnv(extraEnv) {
|
|
4
|
+
return {
|
|
5
|
+
...process.env,
|
|
6
|
+
...extraEnv,
|
|
7
|
+
PATH: `${process.env.HOME}/.npm-global/bin:${process.env.PATH}`,
|
|
8
|
+
CLOUDSDK_CORE_DISABLE_PROMPTS: process.env.CLOUDSDK_CORE_DISABLE_PROMPTS ?? '1',
|
|
9
|
+
CLOUDSDK_PAGER: process.env.CLOUDSDK_PAGER ?? '',
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function waitForProcessCompletion(proc, timeoutMs, drainMs = DEFAULT_DRAIN_MS) {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
let settled = false;
|
|
15
|
+
let timedOut = false;
|
|
16
|
+
let exitCode = null;
|
|
17
|
+
let signal = null;
|
|
18
|
+
let timeoutHandle = null;
|
|
19
|
+
let finalizeTimer = null;
|
|
20
|
+
const finish = () => {
|
|
21
|
+
if (settled)
|
|
22
|
+
return;
|
|
23
|
+
settled = true;
|
|
24
|
+
if (timeoutHandle)
|
|
25
|
+
clearTimeout(timeoutHandle);
|
|
26
|
+
if (finalizeTimer)
|
|
27
|
+
clearTimeout(finalizeTimer);
|
|
28
|
+
resolve({ exitCode, signal, timedOut });
|
|
29
|
+
};
|
|
30
|
+
if (timeoutMs > 0) {
|
|
31
|
+
timeoutHandle = setTimeout(() => {
|
|
32
|
+
timedOut = true;
|
|
33
|
+
proc.kill('SIGTERM');
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
if (proc.exitCode === null) {
|
|
36
|
+
proc.kill('SIGKILL');
|
|
37
|
+
}
|
|
38
|
+
}, FORCE_KILL_DELAY_MS);
|
|
39
|
+
}, timeoutMs);
|
|
40
|
+
}
|
|
41
|
+
proc.on('exit', (code, exitSignal) => {
|
|
42
|
+
exitCode = code;
|
|
43
|
+
signal = exitSignal;
|
|
44
|
+
if (finalizeTimer)
|
|
45
|
+
clearTimeout(finalizeTimer);
|
|
46
|
+
finalizeTimer = setTimeout(finish, Math.max(0, drainMs));
|
|
47
|
+
});
|
|
48
|
+
proc.on('close', (code, closeSignal) => {
|
|
49
|
+
if (exitCode === null)
|
|
50
|
+
exitCode = code;
|
|
51
|
+
if (signal === null)
|
|
52
|
+
signal = closeSignal;
|
|
53
|
+
finish();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/tools/process.ts"],"names":[],"mappings":"AAEA,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAQlC,MAAM,UAAU,QAAQ,CAAC,QAAiC;IACxD,OAAO;QACL,GAAG,OAAO,CAAC,GAAG;QACd,GAAG,QAAQ;QACX,IAAI,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,oBAAoB,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE;QAC/D,6BAA6B,EAAE,OAAO,CAAC,GAAG,CAAC,6BAA6B,IAAI,GAAG;QAC/E,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE;KACjD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,IAAoC,EACpC,SAAiB,EACjB,OAAO,GAAG,gBAAgB;IAE1B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,QAAQ,GAAkB,IAAI,CAAC;QACnC,IAAI,MAAM,GAA0B,IAAI,CAAC;QACzC,IAAI,aAAa,GAA0B,IAAI,CAAC;QAChD,IAAI,aAAa,GAA0B,IAAI,CAAC;QAEhD,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,IAAI,aAAa;gBAAE,YAAY,CAAC,aAAa,CAAC,CAAC;YAC/C,IAAI,aAAa;gBAAE,YAAY,CAAC,aAAa,CAAC,CAAC;YAC/C,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC1C,CAAC,CAAC;QAEF,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC9B,QAAQ,GAAG,IAAI,CAAC;gBAChB,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACrB,UAAU,CAAC,GAAG,EAAE;oBACd,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;wBAC3B,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBACvB,CAAC;gBACH,CAAC,EAAE,mBAAmB,CAAC,CAAC;YAC1B,CAAC,EAAE,SAAS,CAAC,CAAC;QAChB,CAAC;QAED,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE;YACnC,QAAQ,GAAG,IAAI,CAAC;YAChB,MAAM,GAAG,UAAU,CAAC;YACpB,IAAI,aAAa;gBAAE,YAAY,CAAC,aAAa,CAAC,CAAC;YAC/C,aAAa,GAAG,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE;YACrC,IAAI,QAAQ,KAAK,IAAI;gBAAE,QAAQ,GAAG,IAAI,CAAC;YACvC,IAAI,MAAM,KAAK,IAAI;gBAAE,MAAM,GAAG,WAAW,CAAC;YAC1C,MAAM,EAAE,CAAC;QACX,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
const MAX_OUTPUT_CHARS = 16_000;
|
|
3
|
+
const DEFAULT_READ_TIMEOUT_MS = 1_000;
|
|
4
|
+
const sessions = new Map();
|
|
5
|
+
function getOrCreate(id) {
|
|
6
|
+
const existing = sessions.get(id);
|
|
7
|
+
if (existing && !existing.exited)
|
|
8
|
+
return existing;
|
|
9
|
+
const proc = spawn('bash', ['-l'], {
|
|
10
|
+
cwd: process.cwd(),
|
|
11
|
+
env: {
|
|
12
|
+
...process.env,
|
|
13
|
+
PATH: `${process.env.HOME}/.npm-global/bin:${process.env.PATH}`,
|
|
14
|
+
TERM: 'dumb',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
const session = {
|
|
18
|
+
proc,
|
|
19
|
+
output: '',
|
|
20
|
+
exited: false,
|
|
21
|
+
exitCode: null,
|
|
22
|
+
};
|
|
23
|
+
proc.stdout.on('data', (data) => {
|
|
24
|
+
session.output += data.toString();
|
|
25
|
+
});
|
|
26
|
+
proc.stderr.on('data', (data) => {
|
|
27
|
+
session.output += data.toString();
|
|
28
|
+
});
|
|
29
|
+
proc.on('exit', (code) => {
|
|
30
|
+
session.exited = true;
|
|
31
|
+
session.exitCode = code;
|
|
32
|
+
});
|
|
33
|
+
sessions.set(id, session);
|
|
34
|
+
return session;
|
|
35
|
+
}
|
|
36
|
+
function truncateOutput(output) {
|
|
37
|
+
if (output.length <= MAX_OUTPUT_CHARS)
|
|
38
|
+
return output;
|
|
39
|
+
const notice = `[output truncated; showing latest ${MAX_OUTPUT_CHARS} chars]\n`;
|
|
40
|
+
return notice + output.slice(-MAX_OUTPUT_CHARS + notice.length);
|
|
41
|
+
}
|
|
42
|
+
export const terminalTools = [
|
|
43
|
+
{
|
|
44
|
+
name: 'terminal_run',
|
|
45
|
+
definition: {
|
|
46
|
+
name: 'terminal_run',
|
|
47
|
+
description: 'Run a command in a named persistent terminal session. Use for long-running commands, background work, or parallel execution. Output accumulates across runs in the same session.',
|
|
48
|
+
parameters: {
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
session_id: {
|
|
52
|
+
type: 'string',
|
|
53
|
+
description: 'Session identifier (reuse to continue in the same terminal)',
|
|
54
|
+
},
|
|
55
|
+
command: {
|
|
56
|
+
type: 'string',
|
|
57
|
+
description: 'Command to execute (written to stdin followed by a newline)',
|
|
58
|
+
},
|
|
59
|
+
wait_ms: {
|
|
60
|
+
type: 'number',
|
|
61
|
+
description: 'Milliseconds to wait for output after sending the command (default 1000)',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
required: ['session_id', 'command'],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
async execute(input) {
|
|
68
|
+
const { session_id, command, wait_ms } = input;
|
|
69
|
+
const session = getOrCreate(session_id);
|
|
70
|
+
if (session.exited) {
|
|
71
|
+
sessions.delete(session_id);
|
|
72
|
+
return `Session "${session_id}" has exited (code ${session.exitCode}). It will be recreated on next use.`;
|
|
73
|
+
}
|
|
74
|
+
session.output = '';
|
|
75
|
+
session.proc.stdin.write(command + '\n');
|
|
76
|
+
const timeout = Math.max(100, Math.min(wait_ms ?? DEFAULT_READ_TIMEOUT_MS, 300_000));
|
|
77
|
+
await new Promise((resolve) => setTimeout(resolve, timeout));
|
|
78
|
+
return truncateOutput(session.output) || '(no output yet)';
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'terminal_read',
|
|
83
|
+
definition: {
|
|
84
|
+
name: 'terminal_read',
|
|
85
|
+
description: 'Read accumulated output from a terminal session without sending a new command.',
|
|
86
|
+
parameters: {
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {
|
|
89
|
+
session_id: {
|
|
90
|
+
type: 'string',
|
|
91
|
+
description: 'Session identifier to read from',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
required: ['session_id'],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
async execute(input) {
|
|
98
|
+
const { session_id } = input;
|
|
99
|
+
const session = sessions.get(session_id);
|
|
100
|
+
if (!session)
|
|
101
|
+
return `No session "${session_id}" found.`;
|
|
102
|
+
if (session.exited) {
|
|
103
|
+
const output = truncateOutput(session.output);
|
|
104
|
+
sessions.delete(session_id);
|
|
105
|
+
return `Session "${session_id}" has exited (code ${session.exitCode}).\n${output}`;
|
|
106
|
+
}
|
|
107
|
+
const output = session.output;
|
|
108
|
+
session.output = '';
|
|
109
|
+
return truncateOutput(output) || '(no new output)';
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/tools/terminal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export function waitTool(updates) {
|
|
2
|
+
return {
|
|
3
|
+
name: 'wait',
|
|
4
|
+
definition: {
|
|
5
|
+
name: 'wait',
|
|
6
|
+
description: 'Pause execution when there is no immediate action to take. Accepts a duration in minutes, a target ISO datetime, or both — whichever comes first ends the wait. Also ends early if an external event (e.g. incoming message) arrives.',
|
|
7
|
+
parameters: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
minutes: {
|
|
11
|
+
type: 'number',
|
|
12
|
+
description: 'Number of minutes to wait (max 1440)',
|
|
13
|
+
},
|
|
14
|
+
datetime: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
description: 'ISO datetime to wait until (e.g. 2025-01-15T09:00:00Z)',
|
|
17
|
+
},
|
|
18
|
+
reason: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Reason for waiting',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
async execute(input) {
|
|
26
|
+
const { minutes, datetime } = input;
|
|
27
|
+
let waitMs = 60 * 60_000; // default 1 hour
|
|
28
|
+
if (minutes !== undefined) {
|
|
29
|
+
waitMs = Math.max(1, Math.min(minutes, 1440)) * 60_000;
|
|
30
|
+
}
|
|
31
|
+
if (datetime) {
|
|
32
|
+
const target = new Date(datetime).getTime();
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
if (target > now) {
|
|
35
|
+
const untilTarget = target - now;
|
|
36
|
+
waitMs = minutes !== undefined ? Math.min(waitMs, untilTarget) : untilTarget;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const hasUpdate = await updates.waitForUpdate(waitMs);
|
|
40
|
+
const now = new Date().toISOString();
|
|
41
|
+
if (hasUpdate) {
|
|
42
|
+
return `Wait ended early — event arrived. Current time: ${now}.`;
|
|
43
|
+
}
|
|
44
|
+
return `Wait completed. Current time: ${now}.`;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=wait.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait.js","sourceRoot":"","sources":["../../src/tools/wait.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,QAAQ,CAAC,OAAoB;IAC3C,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,UAAU,EAAE;YACV,IAAI,EAAE,MAAM;YACZ,WAAW,EACT,uOAAuO;YACzO,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,OAAO,EAAE;wBACP,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,sCAAsC;qBACpD;oBACD,QAAQ,EAAE;wBACR,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,wDAAwD;qBACtE;oBACD,MAAM,EAAE;wBACN,IAAI,EAAE,QAAQ;wBACd,WAAW,EAAE,oBAAoB;qBAClC;iBACF;aACF;SACF;QACD,KAAK,CAAC,OAAO,CAAC,KAAK;YACjB,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,KAI7B,CAAC;YAEF,IAAI,MAAM,GAAG,EAAE,GAAG,MAAM,CAAC,CAAC,iBAAiB;YAE3C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC;YACzD,CAAC;YAED,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBACvB,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;oBACjB,MAAM,WAAW,GAAG,MAAM,GAAG,GAAG,CAAC;oBACjC,MAAM,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;gBAC/E,CAAC;YACH,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YACtD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAErC,IAAI,SAAS,EAAE,CAAC;gBACd,OAAO,mDAAmD,GAAG,GAAG,CAAC;YACnE,CAAC;YACD,OAAO,iCAAiC,GAAG,GAAG,CAAC;QACjD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/turns.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { sanitizeHistory } from './types.js';
|
|
4
|
+
import { log } from './logs.js';
|
|
5
|
+
const MAX_MESSAGE_TEXT_CHARS = 32_000;
|
|
6
|
+
const MAX_TOOL_RESULT_CHARS = 16_000;
|
|
7
|
+
const MAX_TURN_INPUT_CHARS = 320_000;
|
|
8
|
+
const MIN_MESSAGES_AFTER_COMPACTION = 20;
|
|
9
|
+
function truncateText(text, maxChars, keep) {
|
|
10
|
+
if (text.length <= maxChars)
|
|
11
|
+
return text;
|
|
12
|
+
const notice = keep === 'end'
|
|
13
|
+
? `[content truncated before this point; kept latest ${maxChars} chars]\n`
|
|
14
|
+
: `[content truncated after this point; kept first ${maxChars} chars]\n`;
|
|
15
|
+
const keptChars = Math.max(0, maxChars - notice.length);
|
|
16
|
+
if (keep === 'end') {
|
|
17
|
+
return notice + text.slice(-keptChars);
|
|
18
|
+
}
|
|
19
|
+
return notice + text.slice(0, keptChars);
|
|
20
|
+
}
|
|
21
|
+
function estimateInputChars(input) {
|
|
22
|
+
let total = input.systemPrompt.length;
|
|
23
|
+
for (const msg of input.messages) {
|
|
24
|
+
total += msg.text?.length ?? 0;
|
|
25
|
+
if (msg.toolCalls) {
|
|
26
|
+
for (const call of msg.toolCalls) {
|
|
27
|
+
total += call.name.length;
|
|
28
|
+
try {
|
|
29
|
+
total += JSON.stringify(call.args).length;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
total += 100;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (msg.toolResults) {
|
|
37
|
+
for (const result of msg.toolResults) {
|
|
38
|
+
total += result.result.length;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return total;
|
|
43
|
+
}
|
|
44
|
+
export function maybeTruncateInput(input) {
|
|
45
|
+
const messages = input.messages.map((msg) => ({
|
|
46
|
+
...msg,
|
|
47
|
+
text: msg.text ? truncateText(msg.text, MAX_MESSAGE_TEXT_CHARS, 'end') : undefined,
|
|
48
|
+
toolResults: msg.toolResults?.map((result) => ({
|
|
49
|
+
...result,
|
|
50
|
+
result: truncateText(result.result, MAX_TOOL_RESULT_CHARS, 'end'),
|
|
51
|
+
})),
|
|
52
|
+
}));
|
|
53
|
+
const compacted = { ...input, messages };
|
|
54
|
+
let dropped = 0;
|
|
55
|
+
while (estimateInputChars(compacted) > MAX_TURN_INPUT_CHARS &&
|
|
56
|
+
compacted.messages.length > MIN_MESSAGES_AFTER_COMPACTION) {
|
|
57
|
+
compacted.messages.shift();
|
|
58
|
+
dropped++;
|
|
59
|
+
}
|
|
60
|
+
if (dropped > 0) {
|
|
61
|
+
compacted.messages = sanitizeHistory(compacted.messages);
|
|
62
|
+
const now = new Date().toISOString();
|
|
63
|
+
compacted.messages.unshift({
|
|
64
|
+
role: 'user',
|
|
65
|
+
text: `[Context compacted before model call at ${now} — ${dropped} older messages dropped due to input size.]`,
|
|
66
|
+
});
|
|
67
|
+
log(`input compacted before model call (${dropped} messages dropped)`);
|
|
68
|
+
}
|
|
69
|
+
return compacted;
|
|
70
|
+
}
|
|
71
|
+
export function trimHistory(messages, maxMessages) {
|
|
72
|
+
if (messages.length <= maxMessages)
|
|
73
|
+
return messages;
|
|
74
|
+
const dropped = messages.length - maxMessages;
|
|
75
|
+
const compacted = sanitizeHistory(messages.slice(-maxMessages));
|
|
76
|
+
compacted.unshift({
|
|
77
|
+
role: 'user',
|
|
78
|
+
text: `[Context compacted at ${new Date().toISOString()} — ${dropped} older messages were dropped. Check that anything important from the dropped context has been persisted to mind.]`,
|
|
79
|
+
});
|
|
80
|
+
return compacted.slice(-maxMessages);
|
|
81
|
+
}
|
|
82
|
+
export function buildUserTurnInput(updates) {
|
|
83
|
+
const lines = updates.map((update) => `- [${update.timestamp}] (${update.source}) id=${update.id}: ${update.text}`);
|
|
84
|
+
return `Incoming updates:\n${lines.join('\n')}`;
|
|
85
|
+
}
|
|
86
|
+
export class TurnStore {
|
|
87
|
+
dir;
|
|
88
|
+
constructor(stateDir) {
|
|
89
|
+
this.dir = path.join(stateDir, 'turns');
|
|
90
|
+
}
|
|
91
|
+
async save(data) {
|
|
92
|
+
try {
|
|
93
|
+
await fs.mkdir(this.dir, { recursive: true });
|
|
94
|
+
const filename = data.ts.replace(/[:.]/g, '-') + '.json';
|
|
95
|
+
await fs.writeFile(path.join(this.dir, filename), JSON.stringify(data));
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Turn persistence is best-effort; don't crash the agent loop.
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=turns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turns.js","sourceRoot":"","sources":["../src/turns.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function formatTime() {
|
|
2
|
+
return new Date().toISOString();
|
|
3
|
+
}
|
|
4
|
+
export function sanitizeHistory(messages) {
|
|
5
|
+
if (messages.length === 0)
|
|
6
|
+
return messages;
|
|
7
|
+
if (messages[0]?.role === 'assistant') {
|
|
8
|
+
return messages.slice(1);
|
|
9
|
+
}
|
|
10
|
+
return messages;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA6FA,MAAM,UAAU,UAAU;IACxB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAmB;IACjD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC3C,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;QACtC,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/dist/updates.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export class UpdateQueue {
|
|
2
|
+
items = [];
|
|
3
|
+
waiters = [];
|
|
4
|
+
push(update) {
|
|
5
|
+
this.items.push(update);
|
|
6
|
+
if (this.waiters.length > 0) {
|
|
7
|
+
const pending = this.waiters;
|
|
8
|
+
this.waiters = [];
|
|
9
|
+
for (const wake of pending) {
|
|
10
|
+
wake();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
drain() {
|
|
15
|
+
if (this.items.length === 0)
|
|
16
|
+
return [];
|
|
17
|
+
return this.items.splice(0, this.items.length);
|
|
18
|
+
}
|
|
19
|
+
get length() {
|
|
20
|
+
return this.items.length;
|
|
21
|
+
}
|
|
22
|
+
async waitForUpdate(timeoutMs) {
|
|
23
|
+
if (this.items.length > 0)
|
|
24
|
+
return true;
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
let done = false;
|
|
27
|
+
const timer = setTimeout(() => {
|
|
28
|
+
if (done)
|
|
29
|
+
return;
|
|
30
|
+
done = true;
|
|
31
|
+
resolve(false);
|
|
32
|
+
}, timeoutMs);
|
|
33
|
+
this.waiters.push(() => {
|
|
34
|
+
if (done)
|
|
35
|
+
return;
|
|
36
|
+
done = true;
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
resolve(true);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
wake() {
|
|
43
|
+
const pending = this.waiters;
|
|
44
|
+
this.waiters = [];
|
|
45
|
+
for (const wake of pending) {
|
|
46
|
+
wake();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function formatUpdates(updates) {
|
|
51
|
+
if (updates.length === 0)
|
|
52
|
+
return '';
|
|
53
|
+
const lines = updates.map((u) => `- [${u.timestamp}] (${u.source}) id=${u.id}: ${u.text}`);
|
|
54
|
+
return `Incoming updates:\n${lines.join('\n')}`;
|
|
55
|
+
}
|
|
56
|
+
export function collectImages(updates) {
|
|
57
|
+
const images = [];
|
|
58
|
+
for (const update of updates) {
|
|
59
|
+
if (update.images) {
|
|
60
|
+
images.push(...update.images);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return images;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=updates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updates.js","sourceRoot":"","sources":["../src/updates.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,WAAW;IACL,KAAK,GAAoB,EAAE,CAAC;IACrC,OAAO,GAAsB,EAAE,CAAC;IAExC,IAAI,CAAC,MAAqB;QACxB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;YAC7B,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;YAClB,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;gBAC3B,IAAI,EAAE,CAAC;YACT,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QACvC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,SAAiB;QACnC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACvC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,IAAI,GAAG,KAAK,CAAC;YACjB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,IAAI;oBAAE,OAAO;gBACjB,IAAI,GAAG,IAAI,CAAC;gBACZ,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC,EAAE,SAAS,CAAC,CAAC;YACd,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE;gBACrB,IAAI,IAAI;oBAAE,OAAO;gBACjB,IAAI,GAAG,IAAI,CAAC;gBACZ,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI;QACF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAClB,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,EAAE,CAAC;QACT,CAAC;IACH,CAAC;CACF;AAED,MAAM,UAAU,aAAa,CAAC,OAAwB;IACpD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,MAAM,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3F,OAAO,sBAAsB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAwB;IACpD,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|