@agentworkforce/mcp-workforce 0.0.0 → 3.0.2
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/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/config.d.ts +42 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +39 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +2 -0
- package/dist/config.test.d.ts.map +1 -0
- package/dist/config.test.js +47 -0
- package/dist/config.test.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +32 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +167 -0
- package/dist/server.js.map +1 -0
- package/dist/server.test.d.ts +2 -0
- package/dist/server.test.d.ts.map +1 -0
- package/dist/server.test.js +48 -0
- package/dist/server.test.js.map +1 -0
- package/dist/tools/integrations.d.ts +30 -0
- package/dist/tools/integrations.d.ts.map +1 -0
- package/dist/tools/integrations.js +133 -0
- package/dist/tools/integrations.js.map +1 -0
- package/dist/tools/integrations.test.d.ts +2 -0
- package/dist/tools/integrations.test.d.ts.map +1 -0
- package/dist/tools/integrations.test.js +77 -0
- package/dist/tools/integrations.test.js.map +1 -0
- package/dist/tools/memory.d.ts +36 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +129 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/memory.test.d.ts +2 -0
- package/dist/tools/memory.test.d.ts.map +1 -0
- package/dist/tools/memory.test.js +100 -0
- package/dist/tools/memory.test.js.map +1 -0
- package/dist/tools/workflow.d.ts +31 -0
- package/dist/tools/workflow.d.ts.map +1 -0
- package/dist/tools/workflow.js +58 -0
- package/dist/tools/workflow.js.map +1 -0
- package/dist/tools/workflow.test.d.ts +2 -0
- package/dist/tools/workflow.test.d.ts.map +1 -0
- package/dist/tools/workflow.test.js +74 -0
- package/dist/tools/workflow.test.js.map +1 -0
- package/package.json +8 -8
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { createGithubClient } from '@agentworkforce/runtime';
|
|
2
|
+
const clientCache = {};
|
|
3
|
+
/**
|
|
4
|
+
* Top-level dispatcher. `tool` is a string like `integration.github.comment`;
|
|
5
|
+
* args is whatever JSON the MCP caller sent. Returns the provider client
|
|
6
|
+
* method's return value (or whatever JSON-safe shape it produced).
|
|
7
|
+
*/
|
|
8
|
+
export async function dispatchIntegration(tool, args, deps) {
|
|
9
|
+
const segments = tool.split('.');
|
|
10
|
+
if (segments.length !== 3 || segments[0] !== 'integration') {
|
|
11
|
+
throw new Error(`integration tool name must be "integration.<provider>.<method>"; got "${tool}"`);
|
|
12
|
+
}
|
|
13
|
+
const [, provider, method] = segments;
|
|
14
|
+
switch (provider) {
|
|
15
|
+
case 'github':
|
|
16
|
+
return invokeGithub(method, args, deps);
|
|
17
|
+
default:
|
|
18
|
+
throw new Error(`integration provider "${provider}" is not wired into the MCP server yet (only github ships in v1)`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Static list of available tool names — used for MCP listTools discovery. */
|
|
22
|
+
export const INTEGRATION_TOOL_NAMES = [
|
|
23
|
+
'integration.github.comment',
|
|
24
|
+
'integration.github.createIssue',
|
|
25
|
+
'integration.github.upsertIssue',
|
|
26
|
+
'integration.github.getPr',
|
|
27
|
+
'integration.github.postReview'
|
|
28
|
+
];
|
|
29
|
+
async function invokeGithub(method, args, deps) {
|
|
30
|
+
const client = resolveGithub(deps);
|
|
31
|
+
switch (method) {
|
|
32
|
+
case 'comment': {
|
|
33
|
+
const { target, body } = asObject(args, 'integration.github.comment');
|
|
34
|
+
return client.comment(asTarget(target), asNonEmptyString(body, 'body'));
|
|
35
|
+
}
|
|
36
|
+
case 'createIssue': {
|
|
37
|
+
const fields = asObject(args, 'integration.github.createIssue');
|
|
38
|
+
return client.createIssue({
|
|
39
|
+
owner: asNonEmptyString(fields.owner, 'owner'),
|
|
40
|
+
repo: asNonEmptyString(fields.repo, 'repo'),
|
|
41
|
+
title: asNonEmptyString(fields.title, 'title'),
|
|
42
|
+
body: asNonEmptyString(fields.body, 'body'),
|
|
43
|
+
...(Array.isArray(fields.labels) ? { labels: fields.labels.filter(isString) } : {})
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
case 'upsertIssue': {
|
|
47
|
+
const fields = asObject(args, 'integration.github.upsertIssue');
|
|
48
|
+
return client.upsertIssue({
|
|
49
|
+
owner: asNonEmptyString(fields.owner, 'owner'),
|
|
50
|
+
repo: asNonEmptyString(fields.repo, 'repo'),
|
|
51
|
+
title: asNonEmptyString(fields.title, 'title'),
|
|
52
|
+
body: asNonEmptyString(fields.body, 'body'),
|
|
53
|
+
matchTitle: asNonEmptyString(fields.matchTitle, 'matchTitle'),
|
|
54
|
+
...(Array.isArray(fields.labels) ? { labels: fields.labels.filter(isString) } : {})
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
case 'getPr': {
|
|
58
|
+
const { target } = asObject(args, 'integration.github.getPr');
|
|
59
|
+
return client.getPr(asTarget(target));
|
|
60
|
+
}
|
|
61
|
+
case 'postReview': {
|
|
62
|
+
const { target, review } = asObject(args, 'integration.github.postReview');
|
|
63
|
+
const reviewObj = asObject(review, 'review');
|
|
64
|
+
const eventField = reviewObj.event;
|
|
65
|
+
if (eventField !== 'COMMENT' && eventField !== 'APPROVE' && eventField !== 'REQUEST_CHANGES') {
|
|
66
|
+
throw new Error('review.event must be one of: COMMENT, APPROVE, REQUEST_CHANGES');
|
|
67
|
+
}
|
|
68
|
+
return client.postReview(asTarget(target), {
|
|
69
|
+
body: asNonEmptyString(reviewObj.body, 'review.body'),
|
|
70
|
+
event: eventField,
|
|
71
|
+
...(Array.isArray(reviewObj.comments)
|
|
72
|
+
? {
|
|
73
|
+
comments: reviewObj.comments
|
|
74
|
+
.filter((c) => typeof c === 'object' && c !== null)
|
|
75
|
+
.map((c) => ({
|
|
76
|
+
path: asNonEmptyString(c.path, 'comment.path'),
|
|
77
|
+
line: asNumber(c.line, 'comment.line'),
|
|
78
|
+
body: asNonEmptyString(c.body, 'comment.body')
|
|
79
|
+
}))
|
|
80
|
+
}
|
|
81
|
+
: {})
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
default:
|
|
85
|
+
throw new Error(`integration.github.${method} is not implemented`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function resolveGithub(deps) {
|
|
89
|
+
if (clientCache.github)
|
|
90
|
+
return clientCache.github;
|
|
91
|
+
if (!deps.config.relayfileMountRoot) {
|
|
92
|
+
throw new Error('integration.github is not configured: RELAYFILE_MOUNT_ROOT is required so the github client can write drafts into the Relayfile mount. The workforce runtime sets this automatically when spawning the harness via ctx.harness.run.');
|
|
93
|
+
}
|
|
94
|
+
clientCache.github = createGithubClient({
|
|
95
|
+
relayfileMountRoot: deps.config.relayfileMountRoot,
|
|
96
|
+
writebackTimeoutMs: deps.config.writebackTimeoutMs
|
|
97
|
+
});
|
|
98
|
+
return clientCache.github;
|
|
99
|
+
}
|
|
100
|
+
function asObject(value, label) {
|
|
101
|
+
if (typeof value !== 'object' || value === null) {
|
|
102
|
+
throw new Error(`${label}: expected an object argument`);
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
function asNonEmptyString(value, label) {
|
|
107
|
+
if (typeof value !== 'string' || !value.trim()) {
|
|
108
|
+
throw new Error(`${label}: must be a non-empty string`);
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
function asNumber(value, label) {
|
|
113
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
114
|
+
throw new Error(`${label}: must be a finite number`);
|
|
115
|
+
}
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
function isString(value) {
|
|
119
|
+
return typeof value === 'string';
|
|
120
|
+
}
|
|
121
|
+
function asTarget(value) {
|
|
122
|
+
const obj = asObject(value, 'target');
|
|
123
|
+
return {
|
|
124
|
+
owner: asNonEmptyString(obj.owner, 'target.owner'),
|
|
125
|
+
repo: asNonEmptyString(obj.repo, 'target.repo'),
|
|
126
|
+
number: asNumber(obj.number, 'target.number')
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** Reset the lazy client cache. Test-only — production has one server lifetime. */
|
|
130
|
+
export function _resetIntegrationCache() {
|
|
131
|
+
delete clientCache.github;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=integrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrations.js","sourceRoot":"","sources":["../../src/tools/integrations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrations.test.d.ts","sourceRoot":"","sources":["../../src/tools/integrations.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, readdir, readFile, rm } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { dispatchIntegration, _resetIntegrationCache } from './integrations.js';
|
|
7
|
+
async function tempMount() {
|
|
8
|
+
return mkdtemp(path.join(tmpdir(), 'mcp-workforce-int-'));
|
|
9
|
+
}
|
|
10
|
+
function config(over = {}) {
|
|
11
|
+
return {
|
|
12
|
+
workspaceId: 'ws-demo',
|
|
13
|
+
cloudUrl: 'https://cloud.example.com',
|
|
14
|
+
writebackTimeoutMs: 0,
|
|
15
|
+
...over
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
test('dispatchIntegration rejects malformed tool names', async () => {
|
|
19
|
+
_resetIntegrationCache();
|
|
20
|
+
await assert.rejects(() => dispatchIntegration('integration.github', {}, { config: config() }), /must be "integration\.<provider>\.<method>"/);
|
|
21
|
+
await assert.rejects(() => dispatchIntegration('memory.save', {}, { config: config() }), /must be "integration\.<provider>\.<method>"/);
|
|
22
|
+
});
|
|
23
|
+
test('dispatchIntegration rejects unwired providers', async () => {
|
|
24
|
+
_resetIntegrationCache();
|
|
25
|
+
await assert.rejects(() => dispatchIntegration('integration.linear.createIssue', {}, { config: config() }), /integration provider "linear" is not wired/);
|
|
26
|
+
});
|
|
27
|
+
test('dispatchIntegration rejects when RELAYFILE_MOUNT_ROOT is missing', async () => {
|
|
28
|
+
_resetIntegrationCache();
|
|
29
|
+
await assert.rejects(() => dispatchIntegration('integration.github.comment', { target: { owner: 'o', repo: 'r', number: 1 }, body: 'x' }, { config: config() }), /RELAYFILE_MOUNT_ROOT is required/);
|
|
30
|
+
});
|
|
31
|
+
test('dispatchIntegration writes a github comment draft under the Relayfile mount', async () => {
|
|
32
|
+
_resetIntegrationCache();
|
|
33
|
+
const mount = await tempMount();
|
|
34
|
+
try {
|
|
35
|
+
const result = (await dispatchIntegration('integration.github.comment', { target: { owner: 'o', repo: 'r', number: 1 }, body: 'hello' }, { config: config({ relayfileMountRoot: mount }) }));
|
|
36
|
+
// The Relayfile writeback worker would populate the receipt; with
|
|
37
|
+
// writebackTimeoutMs=0 the client returns immediately and the
|
|
38
|
+
// draft path is what we'd expect to see on disk.
|
|
39
|
+
assert.match(result.url, /^\/github\/repos\/o\/r\/issues\/1\/comments\//);
|
|
40
|
+
const commentsDir = path.join(mount, 'github/repos/o/r/issues/1/comments');
|
|
41
|
+
const drafts = await readdir(commentsDir);
|
|
42
|
+
assert.equal(drafts.length, 1);
|
|
43
|
+
assert.deepEqual(JSON.parse(await readFile(path.join(commentsDir, drafts[0] ?? ''), 'utf8')), {
|
|
44
|
+
body: 'hello'
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
await rm(mount, { recursive: true, force: true });
|
|
49
|
+
_resetIntegrationCache();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
test('dispatchIntegration validates github.postReview event enum', async () => {
|
|
53
|
+
_resetIntegrationCache();
|
|
54
|
+
const mount = await tempMount();
|
|
55
|
+
try {
|
|
56
|
+
await assert.rejects(() => dispatchIntegration('integration.github.postReview', {
|
|
57
|
+
target: { owner: 'o', repo: 'r', number: 1 },
|
|
58
|
+
review: { body: 'lgtm', event: 'WAVE' }
|
|
59
|
+
}, { config: config({ relayfileMountRoot: mount }) }), /review\.event must be one of/);
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await rm(mount, { recursive: true, force: true });
|
|
63
|
+
_resetIntegrationCache();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
test('dispatchIntegration surfaces missing required fields with field-pointed errors', async () => {
|
|
67
|
+
_resetIntegrationCache();
|
|
68
|
+
const mount = await tempMount();
|
|
69
|
+
try {
|
|
70
|
+
await assert.rejects(() => dispatchIntegration('integration.github.createIssue', { owner: 'o', repo: '', title: 't', body: 'b' }, { config: config({ relayfileMountRoot: mount }) }), /repo: must be a non-empty string/);
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
await rm(mount, { recursive: true, force: true });
|
|
74
|
+
_resetIntegrationCache();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=integrations.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrations.test.js","sourceRoot":"","sources":["../../src/tools/integrations.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { PersonaMemoryScope } from '@agentworkforce/persona-kit';
|
|
2
|
+
import type { WorkforceMcpConfig } from '../config.js';
|
|
3
|
+
export interface MemoryItem {
|
|
4
|
+
id: string;
|
|
5
|
+
content: string;
|
|
6
|
+
tags: string[];
|
|
7
|
+
scope: PersonaMemoryScope;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
}
|
|
10
|
+
export interface MemorySaveArgs {
|
|
11
|
+
content: string;
|
|
12
|
+
tags?: string[];
|
|
13
|
+
scope?: PersonaMemoryScope;
|
|
14
|
+
}
|
|
15
|
+
export interface MemoryRecallArgs {
|
|
16
|
+
query: string;
|
|
17
|
+
limit?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface MemoryToolDeps {
|
|
20
|
+
config: WorkforceMcpConfig;
|
|
21
|
+
fetchImpl?: typeof fetch;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* `memory.save` — writes a memory entry under the active workspace. Tags
|
|
25
|
+
* include the supplied tags plus a synthetic `workspace:<id>` tag and a
|
|
26
|
+
* `scope:<scope>` tag so callers can filter on recall.
|
|
27
|
+
*/
|
|
28
|
+
export declare function memorySave(args: MemorySaveArgs, deps: MemoryToolDeps): Promise<{
|
|
29
|
+
ok: true;
|
|
30
|
+
id: string;
|
|
31
|
+
}>;
|
|
32
|
+
/** `memory.recall` — semantic search over the workspace's memory bag. */
|
|
33
|
+
export declare function memoryRecall(args: MemoryRecallArgs, deps: MemoryToolDeps): Promise<{
|
|
34
|
+
items: MemoryItem[];
|
|
35
|
+
}>;
|
|
36
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/tools/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAYvD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,kBAAkB,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAQD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAsC9G;AAED,yEAAyE;AACzE,wBAAsB,YAAY,CAChC,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC;IAAE,KAAK,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,CA4ClC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory tools speak directly to the Supermemory REST API so the MCP
|
|
3
|
+
* package stays free of heavy adapter dependencies. The endpoint and
|
|
4
|
+
* payload shape mirror the contract `@agent-assistant/memory` uses, so a
|
|
5
|
+
* workspace's memories written by sage or any other consumer are visible
|
|
6
|
+
* to a harness running under workforce — there's one supermemory project
|
|
7
|
+
* per workspace and we route by tag.
|
|
8
|
+
*/
|
|
9
|
+
const SUPERMEMORY_DEFAULT_ENDPOINT = 'https://api.supermemory.ai';
|
|
10
|
+
const VALID_SCOPES = new Set([
|
|
11
|
+
'workspace',
|
|
12
|
+
'user',
|
|
13
|
+
'global'
|
|
14
|
+
]);
|
|
15
|
+
/**
|
|
16
|
+
* `memory.save` — writes a memory entry under the active workspace. Tags
|
|
17
|
+
* include the supplied tags plus a synthetic `workspace:<id>` tag and a
|
|
18
|
+
* `scope:<scope>` tag so callers can filter on recall.
|
|
19
|
+
*/
|
|
20
|
+
export async function memorySave(args, deps) {
|
|
21
|
+
if (!args.content || !args.content.trim()) {
|
|
22
|
+
throw new Error('memory.save: "content" is required');
|
|
23
|
+
}
|
|
24
|
+
const scope = args.scope ?? 'workspace';
|
|
25
|
+
if (!VALID_SCOPES.has(scope)) {
|
|
26
|
+
throw new Error(`memory.save: invalid scope "${scope}"`);
|
|
27
|
+
}
|
|
28
|
+
requireSupermemoryKey(deps.config);
|
|
29
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
30
|
+
const url = `${memoryEndpoint(deps.config)}/v3/memories`;
|
|
31
|
+
const response = await fetchImpl(url, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: memoryHeaders(deps.config),
|
|
34
|
+
body: JSON.stringify({
|
|
35
|
+
content: args.content,
|
|
36
|
+
containerTag: workspaceContainer(deps.config),
|
|
37
|
+
metadata: {
|
|
38
|
+
workspaceId: deps.config.workspaceId,
|
|
39
|
+
...(deps.config.personaId ? { personaId: deps.config.personaId } : {}),
|
|
40
|
+
scope
|
|
41
|
+
},
|
|
42
|
+
tags: dedupeTags([
|
|
43
|
+
`workspace:${deps.config.workspaceId}`,
|
|
44
|
+
`scope:${scope}`,
|
|
45
|
+
...(args.tags ?? [])
|
|
46
|
+
])
|
|
47
|
+
})
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw await toError(response, 'memory.save');
|
|
51
|
+
}
|
|
52
|
+
const payload = (await response.json());
|
|
53
|
+
if (!payload?.id) {
|
|
54
|
+
throw new Error('memory.save: supermemory response missing id');
|
|
55
|
+
}
|
|
56
|
+
return { ok: true, id: payload.id };
|
|
57
|
+
}
|
|
58
|
+
/** `memory.recall` — semantic search over the workspace's memory bag. */
|
|
59
|
+
export async function memoryRecall(args, deps) {
|
|
60
|
+
if (!args.query || !args.query.trim()) {
|
|
61
|
+
throw new Error('memory.recall: "query" is required');
|
|
62
|
+
}
|
|
63
|
+
const limit = args.limit ?? 5;
|
|
64
|
+
if (!Number.isFinite(limit) || limit <= 0 || limit > 50) {
|
|
65
|
+
throw new Error('memory.recall: "limit" must be 1-50');
|
|
66
|
+
}
|
|
67
|
+
requireSupermemoryKey(deps.config);
|
|
68
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
69
|
+
const url = `${memoryEndpoint(deps.config)}/v3/search`;
|
|
70
|
+
const response = await fetchImpl(url, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: memoryHeaders(deps.config),
|
|
73
|
+
body: JSON.stringify({
|
|
74
|
+
q: args.query,
|
|
75
|
+
containerTag: workspaceContainer(deps.config),
|
|
76
|
+
limit
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
throw await toError(response, 'memory.recall');
|
|
81
|
+
}
|
|
82
|
+
const payload = (await response.json());
|
|
83
|
+
const results = payload.results ?? [];
|
|
84
|
+
const items = results.map((entry) => ({
|
|
85
|
+
id: entry.id,
|
|
86
|
+
content: entry.content ?? entry.memory ?? '',
|
|
87
|
+
tags: entry.tags ?? [],
|
|
88
|
+
scope: entry.metadata?.scope ?? 'workspace',
|
|
89
|
+
createdAt: entry.createdAt ?? entry.created_at ?? ''
|
|
90
|
+
}));
|
|
91
|
+
return { items };
|
|
92
|
+
}
|
|
93
|
+
function workspaceContainer(config) {
|
|
94
|
+
return `workforce:${config.workspaceId}`;
|
|
95
|
+
}
|
|
96
|
+
function memoryEndpoint(config) {
|
|
97
|
+
return (config.supermemoryEndpoint ?? SUPERMEMORY_DEFAULT_ENDPOINT).replace(/\/$/, '');
|
|
98
|
+
}
|
|
99
|
+
function memoryHeaders(config) {
|
|
100
|
+
return {
|
|
101
|
+
accept: 'application/json',
|
|
102
|
+
'content-type': 'application/json',
|
|
103
|
+
authorization: `Bearer ${config.supermemoryApiKey}`,
|
|
104
|
+
'user-agent': 'mcp-workforce'
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function requireSupermemoryKey(config) {
|
|
108
|
+
if (!config.supermemoryApiKey) {
|
|
109
|
+
throw new Error('memory tools require SUPERMEMORY_API_KEY in the env. Set it before spawning the harness, or disable memory tools by not configuring it.');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function dedupeTags(tags) {
|
|
113
|
+
const seen = new Set();
|
|
114
|
+
const out = [];
|
|
115
|
+
for (const tag of tags) {
|
|
116
|
+
const trimmed = tag.trim();
|
|
117
|
+
if (!trimmed || seen.has(trimmed))
|
|
118
|
+
continue;
|
|
119
|
+
seen.add(trimmed);
|
|
120
|
+
out.push(trimmed);
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
async function toError(response, label) {
|
|
125
|
+
const body = await response.text().catch(() => '');
|
|
126
|
+
const excerpt = body.length > 400 ? `${body.slice(0, 400)}…` : body;
|
|
127
|
+
return new Error(`${label}: ${response.status} ${response.statusText}${excerpt ? ` — ${excerpt}` : ''}`);
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/tools/memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.test.d.ts","sourceRoot":"","sources":["../../src/tools/memory.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { memorySave, memoryRecall } from './memory.js';
|
|
4
|
+
function fakeFetch(handlers) {
|
|
5
|
+
const calls = [];
|
|
6
|
+
let i = 0;
|
|
7
|
+
const impl = (async (input, init) => {
|
|
8
|
+
const url = typeof input === 'string' ? input : input.toString();
|
|
9
|
+
const headers = {};
|
|
10
|
+
if (init?.headers) {
|
|
11
|
+
const entries = init.headers instanceof Headers
|
|
12
|
+
? Array.from(init.headers.entries())
|
|
13
|
+
: Array.isArray(init.headers)
|
|
14
|
+
? init.headers
|
|
15
|
+
: Object.entries(init.headers);
|
|
16
|
+
for (const [k, v] of entries)
|
|
17
|
+
headers[k.toLowerCase()] = String(v);
|
|
18
|
+
}
|
|
19
|
+
const body = init?.body ? JSON.parse(init.body.toString()) : undefined;
|
|
20
|
+
const call = { url, method: init?.method ?? 'GET', headers, ...(body !== undefined ? { body } : {}) };
|
|
21
|
+
calls.push(call);
|
|
22
|
+
const handler = handlers[i];
|
|
23
|
+
if (!handler)
|
|
24
|
+
throw new Error(`fakeFetch: no handler at call index ${i}`);
|
|
25
|
+
i += 1;
|
|
26
|
+
return handler(call);
|
|
27
|
+
});
|
|
28
|
+
return { fetch: impl, calls };
|
|
29
|
+
}
|
|
30
|
+
function config(over = {}) {
|
|
31
|
+
return {
|
|
32
|
+
workspaceId: 'ws-demo',
|
|
33
|
+
cloudUrl: 'https://cloud.example.com',
|
|
34
|
+
supermemoryApiKey: 'sm_secret',
|
|
35
|
+
writebackTimeoutMs: 0,
|
|
36
|
+
...over
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
test('memory.save POSTs to supermemory with workspace + scope tags', async () => {
|
|
40
|
+
const { fetch: impl, calls } = fakeFetch([
|
|
41
|
+
() => new Response(JSON.stringify({ id: 'mem-1' }), { status: 200 })
|
|
42
|
+
]);
|
|
43
|
+
const result = await memorySave({ content: 'digest published', tags: ['weekly-digest', 'weekly-digest'] }, { config: config(), fetchImpl: impl });
|
|
44
|
+
assert.equal(result.id, 'mem-1');
|
|
45
|
+
assert.equal(calls[0].url, 'https://api.supermemory.ai/v3/memories');
|
|
46
|
+
assert.equal(calls[0].method, 'POST');
|
|
47
|
+
assert.equal(calls[0].headers.authorization, 'Bearer sm_secret');
|
|
48
|
+
const body = calls[0].body;
|
|
49
|
+
assert.equal(body.content, 'digest published');
|
|
50
|
+
assert.equal(body.containerTag, 'workforce:ws-demo');
|
|
51
|
+
// Tags are deduped + workspace/scope tags injected.
|
|
52
|
+
assert.deepEqual(body.tags, ['workspace:ws-demo', 'scope:workspace', 'weekly-digest']);
|
|
53
|
+
assert.equal(body.metadata.scope, 'workspace');
|
|
54
|
+
});
|
|
55
|
+
test('memory.save rejects unknown scopes', async () => {
|
|
56
|
+
await assert.rejects(() => memorySave({ content: 'x', scope: 'galaxy' }, { config: config() }), /invalid scope/);
|
|
57
|
+
});
|
|
58
|
+
test('memory.recall POSTs to /v3/search and normalizes the response shape', async () => {
|
|
59
|
+
const { fetch: impl, calls } = fakeFetch([
|
|
60
|
+
() => new Response(JSON.stringify({
|
|
61
|
+
results: [
|
|
62
|
+
{
|
|
63
|
+
id: 'm1',
|
|
64
|
+
content: 'note one',
|
|
65
|
+
tags: ['scope:workspace'],
|
|
66
|
+
metadata: { scope: 'workspace' },
|
|
67
|
+
createdAt: '2026-05-12T09:00:00Z'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'm2',
|
|
71
|
+
memory: 'note two',
|
|
72
|
+
created_at: '2026-05-11T08:00:00Z'
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}), { status: 200 })
|
|
76
|
+
]);
|
|
77
|
+
const result = await memoryRecall({ query: 'digest', limit: 5 }, { config: config(), fetchImpl: impl });
|
|
78
|
+
assert.equal(result.items.length, 2);
|
|
79
|
+
assert.equal(result.items[0].id, 'm1');
|
|
80
|
+
assert.equal(result.items[0].content, 'note one');
|
|
81
|
+
// Falls back to `memory` field when `content` is missing.
|
|
82
|
+
assert.equal(result.items[1].content, 'note two');
|
|
83
|
+
// Falls back to default scope and accepts snake_case createdAt.
|
|
84
|
+
assert.equal(result.items[1].scope, 'workspace');
|
|
85
|
+
assert.equal(result.items[1].createdAt, '2026-05-11T08:00:00Z');
|
|
86
|
+
assert.equal(calls[0].url, 'https://api.supermemory.ai/v3/search');
|
|
87
|
+
const body = calls[0].body;
|
|
88
|
+
assert.equal(body.q, 'digest');
|
|
89
|
+
assert.equal(body.containerTag, 'workforce:ws-demo');
|
|
90
|
+
assert.equal(body.limit, 5);
|
|
91
|
+
});
|
|
92
|
+
test('memory.recall enforces a 1-50 limit range', async () => {
|
|
93
|
+
await assert.rejects(() => memoryRecall({ query: 'q', limit: 0 }, { config: config() }), /"limit" must be 1-50/);
|
|
94
|
+
await assert.rejects(() => memoryRecall({ query: 'q', limit: 51 }, { config: config() }), /"limit" must be 1-50/);
|
|
95
|
+
});
|
|
96
|
+
test('memory tools require SUPERMEMORY_API_KEY', async () => {
|
|
97
|
+
await assert.rejects(() => memorySave({ content: 'x' }, { config: config({ supermemoryApiKey: undefined }) }), /SUPERMEMORY_API_KEY/);
|
|
98
|
+
await assert.rejects(() => memoryRecall({ query: 'q' }, { config: config({ supermemoryApiKey: undefined }) }), /SUPERMEMORY_API_KEY/);
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=memory.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.test.js","sourceRoot":"","sources":["../../src/tools/memory.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { WorkforceMcpConfig } from '../config.js';
|
|
2
|
+
/** Public result shape the MCP tool returns. Kept in sync with the workforce cloud workflows REST contract. */
|
|
3
|
+
export interface WorkflowRunResult {
|
|
4
|
+
runId: string;
|
|
5
|
+
status: 'pending' | 'running' | 'success' | 'failure';
|
|
6
|
+
output?: unknown;
|
|
7
|
+
error?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface WorkflowStatusResult {
|
|
10
|
+
status: 'pending' | 'running' | 'success' | 'failure';
|
|
11
|
+
output?: unknown;
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface WorkflowToolDeps {
|
|
15
|
+
config: WorkforceMcpConfig;
|
|
16
|
+
fetchImpl?: typeof fetch;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* `workflow.run` tool. POSTs a workflow invocation to the workforce cloud
|
|
20
|
+
* workflows API; returns immediately with the run id + initial status.
|
|
21
|
+
* Long-running workflows are polled via `workflow.status`.
|
|
22
|
+
*/
|
|
23
|
+
export declare function workflowRun(args: {
|
|
24
|
+
name: string;
|
|
25
|
+
args?: Record<string, unknown>;
|
|
26
|
+
}, deps: WorkflowToolDeps): Promise<WorkflowRunResult>;
|
|
27
|
+
/** `workflow.status` tool — poll a previously-started run. */
|
|
28
|
+
export declare function workflowStatus(args: {
|
|
29
|
+
runId: string;
|
|
30
|
+
}, deps: WorkflowToolDeps): Promise<WorkflowStatusResult>;
|
|
31
|
+
//# sourceMappingURL=workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../../src/tools/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,+GAA+G;AAC/G,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IACtD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IACtD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EACtD,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,iBAAiB,CAAC,CAqB5B;AAED,8DAA8D;AAC9D,wBAAsB,cAAc,CAClC,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EACvB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,oBAAoB,CAAC,CAgB/B"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `workflow.run` tool. POSTs a workflow invocation to the workforce cloud
|
|
3
|
+
* workflows API; returns immediately with the run id + initial status.
|
|
4
|
+
* Long-running workflows are polled via `workflow.status`.
|
|
5
|
+
*/
|
|
6
|
+
export async function workflowRun(args, deps) {
|
|
7
|
+
if (!args.name || !args.name.trim()) {
|
|
8
|
+
throw new Error('workflow.run: "name" is required');
|
|
9
|
+
}
|
|
10
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
11
|
+
const url = `${deps.config.cloudUrl}/api/v1/workspaces/${encodeURIComponent(deps.config.workspaceId)}/workflows/run`;
|
|
12
|
+
const response = await fetchImpl(url, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: workflowHeaders(deps.config),
|
|
15
|
+
body: JSON.stringify({ name: args.name, args: args.args ?? {} })
|
|
16
|
+
});
|
|
17
|
+
if (!response.ok) {
|
|
18
|
+
throw await toError(response, `workflow.run("${args.name}")`);
|
|
19
|
+
}
|
|
20
|
+
const payload = (await response.json());
|
|
21
|
+
if (!payload?.runId) {
|
|
22
|
+
throw new Error(`workflow.run("${args.name}"): cloud response missing runId`);
|
|
23
|
+
}
|
|
24
|
+
return payload;
|
|
25
|
+
}
|
|
26
|
+
/** `workflow.status` tool — poll a previously-started run. */
|
|
27
|
+
export async function workflowStatus(args, deps) {
|
|
28
|
+
if (!args.runId || !args.runId.trim()) {
|
|
29
|
+
throw new Error('workflow.status: "runId" is required');
|
|
30
|
+
}
|
|
31
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
32
|
+
const url = `${deps.config.cloudUrl}/api/v1/workspaces/${encodeURIComponent(deps.config.workspaceId)}/workflows/runs/${encodeURIComponent(args.runId)}`;
|
|
33
|
+
const response = await fetchImpl(url, {
|
|
34
|
+
method: 'GET',
|
|
35
|
+
headers: workflowHeaders(deps.config)
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok) {
|
|
38
|
+
throw await toError(response, `workflow.status("${args.runId}")`);
|
|
39
|
+
}
|
|
40
|
+
return (await response.json());
|
|
41
|
+
}
|
|
42
|
+
function workflowHeaders(config) {
|
|
43
|
+
if (!config.runtimeToken) {
|
|
44
|
+
throw new Error('workflow tool requires WORKFORCE_RUNTIME_TOKEN in the env; the runtime injects this when it spawns the harness');
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
accept: 'application/json',
|
|
48
|
+
'content-type': 'application/json',
|
|
49
|
+
authorization: `Bearer ${config.runtimeToken}`,
|
|
50
|
+
'user-agent': 'mcp-workforce'
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function toError(response, label) {
|
|
54
|
+
const body = await response.text().catch(() => '');
|
|
55
|
+
const excerpt = body.length > 400 ? `${body.slice(0, 400)}…` : body;
|
|
56
|
+
return new Error(`${label}: ${response.status} ${response.statusText}${excerpt ? ` — ${excerpt}` : ''}`);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../src/tools/workflow.ts"],"names":[],"mappings":"AAqBA;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAsD,EACtD,IAAsB;IAEtB,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IAC1C,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,sBAAsB,kBAAkB,CACzE,IAAI,CAAC,MAAM,CAAC,WAAW,CACxB,gBAAgB,CAAC;IAClB,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE;QACpC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC;QACrC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,EAAE,CAAC;KACjE,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,MAAM,OAAO,CAAC,QAAQ,EAAE,iBAAiB,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAsB,CAAC;IAC7D,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,CAAC,IAAI,kCAAkC,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAuB,EACvB,IAAsB;IAEtB,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IAC1C,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,sBAAsB,kBAAkB,CACzE,IAAI,CAAC,MAAM,CAAC,WAAW,CACxB,mBAAmB,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;IACrD,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE;QACpC,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC;KACtC,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,MAAM,OAAO,CAAC,QAAQ,EAAE,oBAAoB,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAyB,CAAC;AACzD,CAAC;AAED,SAAS,eAAe,CAAC,MAA0B;IACjD,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,gHAAgH,CACjH,CAAC;IACJ,CAAC;IACD,OAAO;QACL,MAAM,EAAE,kBAAkB;QAC1B,cAAc,EAAE,kBAAkB;QAClC,aAAa,EAAE,UAAU,MAAM,CAAC,YAAY,EAAE;QAC9C,YAAY,EAAE,eAAe;KAC9B,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,QAAkB,EAAE,KAAa;IACtD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACpE,OAAO,IAAI,KAAK,CACd,GAAG,KAAK,KAAK,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CACvF,CAAC;AACJ,CAAC"}
|