@loomweaver/ag-ui 0.7.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/README.md +47 -0
- package/package.json +45 -0
- package/src/index.d.ts +4 -0
- package/src/index.js +5 -0
- package/src/index.js.map +1 -0
- package/src/lib/command-tools.d.ts +70 -0
- package/src/lib/command-tools.js +107 -0
- package/src/lib/command-tools.js.map +1 -0
- package/src/lib/tool-arguments.d.ts +13 -0
- package/src/lib/tool-arguments.js +49 -0
- package/src/lib/tool-arguments.js.map +1 -0
- package/src/lib/tool-definitions.d.ts +15 -0
- package/src/lib/tool-definitions.js +50 -0
- package/src/lib/tool-definitions.js.map +1 -0
- package/src/lib/tool-results.d.ts +18 -0
- package/src/lib/tool-results.js +40 -0
- package/src/lib/tool-results.js.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @loomweaver/ag-ui
|
|
2
|
+
|
|
3
|
+
Describes the workbench's own commands to an [AG-UI](https://docs.ag-ui.com) agent and runs what it
|
|
4
|
+
asks for, so a weaver keeps no tool registry of its own.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { commandTools } from '@loomweaver/ag-ui';
|
|
8
|
+
|
|
9
|
+
const tools = commandTools(ctx);
|
|
10
|
+
|
|
11
|
+
// when a run starts
|
|
12
|
+
agent.runAgent({ tools: tools.list(), ...rest });
|
|
13
|
+
|
|
14
|
+
// for every event the run produces
|
|
15
|
+
const answer = await tools.receive(event);
|
|
16
|
+
if (answer) messages.push(answer);
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
That is the whole integration. The actions the agent can reach are the ones the workbench already
|
|
20
|
+
knows about, narrowed by everything that would refuse them — so an agent reaches what the user could
|
|
21
|
+
have reached, and nothing more.
|
|
22
|
+
|
|
23
|
+
## What it does not do
|
|
24
|
+
|
|
25
|
+
- **No transport.** Opening a connection, choosing SSE or a socket, retrying and authenticating are
|
|
26
|
+
yours.
|
|
27
|
+
- **No user interface.** Not a chat, not a message list, not a rendering of anything.
|
|
28
|
+
- **No agent.** It never decides what to do; it carries what was decided.
|
|
29
|
+
- **No shared state, reasoning display or subagent handling.** Those events pass by untouched.
|
|
30
|
+
|
|
31
|
+
It is headless and framework-neutral: no Angular, no observables. You bring the stream and hand it
|
|
32
|
+
one event at a time.
|
|
33
|
+
|
|
34
|
+
## Stability
|
|
35
|
+
|
|
36
|
+
**This package's stability follows AG-UI, not the platform.** It shares a version number with the
|
|
37
|
+
other `@loomweaver/*` packages because they are released together, and that number says which platform
|
|
38
|
+
release it was built against. It says nothing about the protocol, which is at `0.0.x` and still
|
|
39
|
+
moving. A break there is answered by publishing at the next platform version.
|
|
40
|
+
|
|
41
|
+
`@ag-ui/core` and `@loomweaver/plugin-sdk` are peer dependencies, so a weaver that also builds its own
|
|
42
|
+
agent resolves one copy of each rather than two.
|
|
43
|
+
|
|
44
|
+
## Documentation
|
|
45
|
+
|
|
46
|
+
The full guide, including the hook that lets you confirm or decline a call before it runs, is at
|
|
47
|
+
[docs/reference/agent-tools.md](https://github.com/yesbert/loomweaver/blob/main/docs/reference/agent-tools.md).
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@loomweaver/ag-ui",
|
|
3
|
+
"version": "0.7.2",
|
|
4
|
+
"description": "LoomWeaver AG-UI adapter: describes the workbench's own commands to an agent and runs what it asks for, so a weaver keeps no tool registry of its own. Headless — no transport, no UI, no agent.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"loomweaver",
|
|
7
|
+
"ag-ui",
|
|
8
|
+
"agent",
|
|
9
|
+
"agentic-ui",
|
|
10
|
+
"tool-calling",
|
|
11
|
+
"plugin-platform"
|
|
12
|
+
],
|
|
13
|
+
"license": "Apache-2.0",
|
|
14
|
+
"author": "Norbert Rosenwinkel (https://loomweaver.dev)",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./src/index.js",
|
|
17
|
+
"module": "./src/index.js",
|
|
18
|
+
"types": "./src/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./src/index.d.ts",
|
|
22
|
+
"default": "./src/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"tslib": "^2.3.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@ag-ui/core": "0.0.x",
|
|
31
|
+
"@loomweaver/plugin-sdk": "0.7.2"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://loomweaver.dev",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/yesbert/loomweaver/issues"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/yesbert/loomweaver.git",
|
|
43
|
+
"directory": "platform/libs/integrations/ag-ui"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { commandTools, type CommandAccess, type CommandToolOptions, type CommandTools, type PendingToolCall, type ToolDecision, } from './lib/command-tools.js';
|
|
2
|
+
export { toolFor, toolsFor } from './lib/tool-definitions.js';
|
|
3
|
+
export { readArguments } from './lib/tool-arguments.js';
|
|
4
|
+
export { resultFor } from './lib/tool-results.js';
|
package/src/index.js
ADDED
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/integrations/ag-ui/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,GAMb,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type BaseEvent, type Tool, type ToolMessage } from '@ag-ui/core';
|
|
2
|
+
import type { CommandArguments, PluginContext } from '@loomweaver/plugin-sdk';
|
|
3
|
+
/**
|
|
4
|
+
* The slice of a plugin context this adapter touches: what may be run, and running it. Narrower than
|
|
5
|
+
* the whole context on purpose — it says exactly what an agent reaches through here, and a plugin
|
|
6
|
+
* hands its `ctx` straight in.
|
|
7
|
+
*/
|
|
8
|
+
export type CommandAccess = Pick<PluginContext, 'invocableCommands' | 'invokeCommand'>;
|
|
9
|
+
/** One call an agent asked for, assembled from however many events carried it. */
|
|
10
|
+
export interface PendingToolCall {
|
|
11
|
+
/** The protocol's id for this call — what the answer is addressed to. */
|
|
12
|
+
readonly toolCallId: string;
|
|
13
|
+
/** The command the agent named. */
|
|
14
|
+
readonly commandId: string;
|
|
15
|
+
/** What it wants to pass, already readable as data but not yet checked against the command. */
|
|
16
|
+
readonly args: CommandArguments;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* What a weaver decides about a call before it runs. `run` lets it through to the workbench,
|
|
20
|
+
* `decline` answers the agent that it did not run and why, and `answer` serves the call without the
|
|
21
|
+
* workbench being involved.
|
|
22
|
+
*
|
|
23
|
+
* A decision can only narrow. Letting a call through does not make it reachable: the workbench
|
|
24
|
+
* refuses what it always refused, whatever was decided here.
|
|
25
|
+
*/
|
|
26
|
+
export type ToolDecision = {
|
|
27
|
+
readonly decision: 'run';
|
|
28
|
+
} | {
|
|
29
|
+
readonly decision: 'decline';
|
|
30
|
+
readonly reason: string;
|
|
31
|
+
} | {
|
|
32
|
+
readonly decision: 'answer';
|
|
33
|
+
readonly content: string;
|
|
34
|
+
};
|
|
35
|
+
/** What a weaver may supply when it wants a say before a call runs. */
|
|
36
|
+
export interface CommandToolOptions {
|
|
37
|
+
before?(call: PendingToolCall): ToolDecision | Promise<ToolDecision>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The workbench's own actions, offered to an agent and run on its behalf.
|
|
41
|
+
*
|
|
42
|
+
* Hand every event of a run to {@link receive} and send back whatever it answers. Ask {@link list}
|
|
43
|
+
* for the tools when a run begins rather than keeping the answer: what a plugin may reach changes as
|
|
44
|
+
* plugins load and the session changes, and a list kept from earlier promises actions that are no
|
|
45
|
+
* longer there.
|
|
46
|
+
*/
|
|
47
|
+
export interface CommandTools {
|
|
48
|
+
/** The tools reachable right now, from the workbench's own already-narrowed account. */
|
|
49
|
+
list(): readonly Tool[];
|
|
50
|
+
/**
|
|
51
|
+
* Takes one event of the run. Answers the message to send back where the event completed a call,
|
|
52
|
+
* and nothing otherwise. Events that are not part of a tool call are ignored.
|
|
53
|
+
*/
|
|
54
|
+
receive(event: BaseEvent): Promise<ToolMessage | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Closes a call the agent left open, answering it if there was one. A run that ends without its
|
|
57
|
+
* closing event is the case this exists for; {@link receive} already does it when the run reports
|
|
58
|
+
* that it finished.
|
|
59
|
+
*/
|
|
60
|
+
flush(): Promise<ToolMessage | null>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Connects a plugin context to an agent's tool calls.
|
|
64
|
+
*
|
|
65
|
+
* The commands the workbench offers become the tools, and a call comes back through the same seam
|
|
66
|
+
* every other trigger runs through — so an agent reaches what the user could have reached, and
|
|
67
|
+
* nothing more. Nothing here opens a connection, renders anything or decides what to do; a weaver
|
|
68
|
+
* brings the stream and this carries what it says.
|
|
69
|
+
*/
|
|
70
|
+
export declare function commandTools(ctx: CommandAccess, options?: CommandToolOptions): CommandTools;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { EventType } from '@ag-ui/core';
|
|
3
|
+
import { readArguments } from './tool-arguments.js';
|
|
4
|
+
import { toolsFor } from './tool-definitions.js';
|
|
5
|
+
import { answerFor, refusalFor, resultFor } from './tool-results.js';
|
|
6
|
+
/**
|
|
7
|
+
* Connects a plugin context to an agent's tool calls.
|
|
8
|
+
*
|
|
9
|
+
* The commands the workbench offers become the tools, and a call comes back through the same seam
|
|
10
|
+
* every other trigger runs through — so an agent reaches what the user could have reached, and
|
|
11
|
+
* nothing more. Nothing here opens a connection, renders anything or decides what to do; a weaver
|
|
12
|
+
* brings the stream and this carries what it says.
|
|
13
|
+
*/
|
|
14
|
+
export function commandTools(ctx, options = {}) {
|
|
15
|
+
const open = new Map();
|
|
16
|
+
let chunked = null;
|
|
17
|
+
const finish = (call) => __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
open.delete(call.toolCallId);
|
|
19
|
+
if (chunked === call.toolCallId) {
|
|
20
|
+
chunked = null;
|
|
21
|
+
}
|
|
22
|
+
const args = readArguments(call.json);
|
|
23
|
+
if (args === null) {
|
|
24
|
+
return refusalFor(call.toolCallId, 'its arguments did not arrive as readable JSON.');
|
|
25
|
+
}
|
|
26
|
+
const decision = yield decide(options, {
|
|
27
|
+
toolCallId: call.toolCallId,
|
|
28
|
+
commandId: call.commandId,
|
|
29
|
+
args,
|
|
30
|
+
});
|
|
31
|
+
if (decision.decision === 'decline') {
|
|
32
|
+
return refusalFor(call.toolCallId, decision.reason);
|
|
33
|
+
}
|
|
34
|
+
if (decision.decision === 'answer') {
|
|
35
|
+
return answerFor(call.toolCallId, decision.content);
|
|
36
|
+
}
|
|
37
|
+
return resultFor(call.toolCallId, yield ctx.invokeCommand(call.commandId, args));
|
|
38
|
+
});
|
|
39
|
+
const flush = () => __awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
const pending = [...open.values()];
|
|
41
|
+
open.clear();
|
|
42
|
+
chunked = null;
|
|
43
|
+
return pending.length === 0 ? null : finish(pending[pending.length - 1]);
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
list: () => toolsFor(ctx.invocableCommands()),
|
|
47
|
+
flush,
|
|
48
|
+
receive: (event) => __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
var _a;
|
|
50
|
+
const raw = event;
|
|
51
|
+
switch (event.type) {
|
|
52
|
+
case EventType.TOOL_CALL_START:
|
|
53
|
+
open.set(String(raw['toolCallId']), {
|
|
54
|
+
toolCallId: String(raw['toolCallId']),
|
|
55
|
+
commandId: String(raw['toolCallName']),
|
|
56
|
+
json: '',
|
|
57
|
+
});
|
|
58
|
+
return null;
|
|
59
|
+
case EventType.TOOL_CALL_ARGS: {
|
|
60
|
+
const call = open.get(String(raw['toolCallId']));
|
|
61
|
+
if (call) {
|
|
62
|
+
call.json += String((_a = raw['delta']) !== null && _a !== void 0 ? _a : '');
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
case EventType.TOOL_CALL_END: {
|
|
67
|
+
const call = open.get(String(raw['toolCallId']));
|
|
68
|
+
return call ? finish(call) : null;
|
|
69
|
+
}
|
|
70
|
+
case EventType.TOOL_CALL_CHUNK:
|
|
71
|
+
return receiveChunk(raw);
|
|
72
|
+
case EventType.RUN_FINISHED:
|
|
73
|
+
case EventType.RUN_ERROR:
|
|
74
|
+
return flush();
|
|
75
|
+
default:
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}),
|
|
79
|
+
};
|
|
80
|
+
function receiveChunk(raw) {
|
|
81
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
82
|
+
var _a, _b, _c;
|
|
83
|
+
const id = raw['toolCallId'];
|
|
84
|
+
if (typeof id === 'string' && id !== chunked) {
|
|
85
|
+
const previous = chunked === null ? null : open.get(chunked);
|
|
86
|
+
chunked = id;
|
|
87
|
+
open.set(id, {
|
|
88
|
+
toolCallId: id,
|
|
89
|
+
commandId: String((_a = raw['toolCallName']) !== null && _a !== void 0 ? _a : ''),
|
|
90
|
+
json: String((_b = raw['delta']) !== null && _b !== void 0 ? _b : ''),
|
|
91
|
+
});
|
|
92
|
+
return previous ? finish(previous) : null;
|
|
93
|
+
}
|
|
94
|
+
const call = chunked === null ? undefined : open.get(chunked);
|
|
95
|
+
if (call) {
|
|
96
|
+
call.json += String((_c = raw['delta']) !== null && _c !== void 0 ? _c : '');
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function decide(options, call) {
|
|
103
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
return options.before ? options.before(call) : { decision: 'run' };
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=command-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-tools.js","sourceRoot":"","sources":["../../../../../../libs/integrations/ag-ui/src/lib/command-tools.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAA+C,MAAM,aAAa,CAAC;AAErF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAsErE;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,GAAkB,EAClB,UAA8B,EAAE;IAEhC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IACzC,IAAI,OAAO,GAAkB,IAAI,CAAC;IAElC,MAAM,MAAM,GAAG,CAAO,IAAc,EAAwB,EAAE;QAC5D,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7B,IAAI,OAAO,KAAK,IAAI,CAAC,UAAU,EAAE,CAAC;YAChC,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;QACD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,OAAO,UAAU,CACf,IAAI,CAAC,UAAU,EACf,gDAAgD,CACjD,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE;YACrC,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,IAAI;SACL,CAAC,CAAC;QACH,IAAI,QAAQ,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACpC,OAAO,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACnC,OAAO,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,SAAS,CACd,IAAI,CAAC,UAAU,EACf,MAAM,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAC9C,CAAC;IACJ,CAAC,CAAA,CAAC;IAEF,MAAM,KAAK,GAAG,GAAsC,EAAE;QACpD,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,GAAG,IAAI,CAAC;QACf,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IAC3E,CAAC,CAAA,CAAC;IAEF,OAAO;QACL,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC;QAC7C,KAAK;QACL,OAAO,EAAE,CAAO,KAAK,EAAE,EAAE;;YACvB,MAAM,GAAG,GAAG,KAAgC,CAAC;YAC7C,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;gBACnB,KAAK,SAAS,CAAC,eAAe;oBAC5B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE;wBAClC,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;wBACrC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;wBACtC,IAAI,EAAE,EAAE;qBACT,CAAC,CAAC;oBACH,OAAO,IAAI,CAAC;gBACd,KAAK,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC;oBAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;oBACjD,IAAI,IAAI,EAAE,CAAC;wBACT,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,MAAA,GAAG,CAAC,OAAO,CAAC,mCAAI,EAAE,CAAC,CAAC;oBAC1C,CAAC;oBACD,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,KAAK,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;oBAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;oBACjD,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBACpC,CAAC;gBACD,KAAK,SAAS,CAAC,eAAe;oBAC5B,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;gBAC3B,KAAK,SAAS,CAAC,YAAY,CAAC;gBAC5B,KAAK,SAAS,CAAC,SAAS;oBACtB,OAAO,KAAK,EAAE,CAAC;gBACjB;oBACE,OAAO,IAAI,CAAC;YAChB,CAAC;QACH,CAAC,CAAA;KACF,CAAC;IAEF,SAAe,YAAY,CACzB,GAA4B;;;YAE5B,MAAM,EAAE,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC;YAC7B,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;gBAC7C,MAAM,QAAQ,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC7D,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE;oBACX,UAAU,EAAE,EAAE;oBACd,SAAS,EAAE,MAAM,CAAC,MAAA,GAAG,CAAC,cAAc,CAAC,mCAAI,EAAE,CAAC;oBAC5C,IAAI,EAAE,MAAM,CAAC,MAAA,GAAG,CAAC,OAAO,CAAC,mCAAI,EAAE,CAAC;iBACjC,CAAC,CAAC;gBACH,OAAO,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC5C,CAAC;YACD,MAAM,IAAI,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC9D,IAAI,IAAI,EAAE,CAAC;gBACT,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,MAAA,GAAG,CAAC,OAAO,CAAC,mCAAI,EAAE,CAAC,CAAC;YAC1C,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;KAAA;AACH,CAAC;AAED,SAAe,MAAM,CACnB,OAA2B,EAC3B,IAAqB;;QAErB,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACrE,CAAC;CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CommandArguments } from '@loomweaver/plugin-sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Reads the JSON an agent streamed for one call into arguments the workbench can be handed, or
|
|
4
|
+
* `null` where it cannot be read as such.
|
|
5
|
+
*
|
|
6
|
+
* This checks only that the values can *cross*, not that they are the ones the command declared.
|
|
7
|
+
* That second check belongs to the workbench and happens there, so a call that survives this one may
|
|
8
|
+
* still be refused for naming an argument the command does not take.
|
|
9
|
+
*
|
|
10
|
+
* An empty stream of deltas reads as a call with no arguments, which is what an agent sends for a
|
|
11
|
+
* command that declares none.
|
|
12
|
+
*/
|
|
13
|
+
export declare function readArguments(json: string): CommandArguments | null;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads the JSON an agent streamed for one call into arguments the workbench can be handed, or
|
|
3
|
+
* `null` where it cannot be read as such.
|
|
4
|
+
*
|
|
5
|
+
* This checks only that the values can *cross*, not that they are the ones the command declared.
|
|
6
|
+
* That second check belongs to the workbench and happens there, so a call that survives this one may
|
|
7
|
+
* still be refused for naming an argument the command does not take.
|
|
8
|
+
*
|
|
9
|
+
* An empty stream of deltas reads as a call with no arguments, which is what an agent sends for a
|
|
10
|
+
* command that declares none.
|
|
11
|
+
*/
|
|
12
|
+
export function readArguments(json) {
|
|
13
|
+
const text = json.trim();
|
|
14
|
+
if (text.length === 0) {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
let parsed;
|
|
18
|
+
try {
|
|
19
|
+
parsed = JSON.parse(text);
|
|
20
|
+
}
|
|
21
|
+
catch (_a) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
if (!isRecord(parsed)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const args = {};
|
|
28
|
+
for (const [name, value] of Object.entries(parsed)) {
|
|
29
|
+
if (isScalar(value)) {
|
|
30
|
+
args[name] = value;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(value) && value.every(isScalar)) {
|
|
34
|
+
args[name] = value;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return args;
|
|
40
|
+
}
|
|
41
|
+
function isRecord(value) {
|
|
42
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
43
|
+
}
|
|
44
|
+
function isScalar(value) {
|
|
45
|
+
return (typeof value === 'string' ||
|
|
46
|
+
typeof value === 'boolean' ||
|
|
47
|
+
(typeof value === 'number' && Number.isFinite(value)));
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=tool-arguments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-arguments.js","sourceRoot":"","sources":["../../../../../../libs/integrations/ag-ui/src/lib/tool-arguments.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACtB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,GAA6D,EAAE,CAAC;IAC1E,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;YACnB,SAAS;QACX,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;YACnB,SAAS;QACX,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,OAAO,KAAK,KAAK,SAAS;QAC1B,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CACtD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Tool } from '@ag-ui/core';
|
|
2
|
+
import type { InvocableCommand } from '@loomweaver/plugin-sdk';
|
|
3
|
+
/**
|
|
4
|
+
* Describes one command the workbench offers as a tool an agent can call. The id becomes the tool
|
|
5
|
+
* name, so a call names the same identity the workbench knows it by, and nothing has to be looked up
|
|
6
|
+
* in a table on the way back.
|
|
7
|
+
*
|
|
8
|
+
* A command with no description of its own gets one derived from its title. The protocol requires a
|
|
9
|
+
* description and an agent chooses between tools by reading it, so an empty string would make the
|
|
10
|
+
* tool unpickable; the title is a poor explanation but it is not nothing, and the workbench warns the
|
|
11
|
+
* author in dev mode when a command is opened without one.
|
|
12
|
+
*/
|
|
13
|
+
export declare function toolFor(command: InvocableCommand): Tool;
|
|
14
|
+
/** Describes every command in the list, in the order the workbench gave them. */
|
|
15
|
+
export declare function toolsFor(commands: readonly InvocableCommand[]): readonly Tool[];
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const SCALAR_TYPE = {
|
|
2
|
+
text: 'string',
|
|
3
|
+
number: 'number',
|
|
4
|
+
boolean: 'boolean',
|
|
5
|
+
choice: 'string',
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Describes one command the workbench offers as a tool an agent can call. The id becomes the tool
|
|
9
|
+
* name, so a call names the same identity the workbench knows it by, and nothing has to be looked up
|
|
10
|
+
* in a table on the way back.
|
|
11
|
+
*
|
|
12
|
+
* A command with no description of its own gets one derived from its title. The protocol requires a
|
|
13
|
+
* description and an agent chooses between tools by reading it, so an empty string would make the
|
|
14
|
+
* tool unpickable; the title is a poor explanation but it is not nothing, and the workbench warns the
|
|
15
|
+
* author in dev mode when a command is opened without one.
|
|
16
|
+
*/
|
|
17
|
+
export function toolFor(command) {
|
|
18
|
+
var _a;
|
|
19
|
+
return {
|
|
20
|
+
name: command.id,
|
|
21
|
+
description: (_a = command.description) !== null && _a !== void 0 ? _a : command.title,
|
|
22
|
+
parameters: parametersFor(command.arguments),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** Describes every command in the list, in the order the workbench gave them. */
|
|
26
|
+
export function toolsFor(commands) {
|
|
27
|
+
return commands.map(toolFor);
|
|
28
|
+
}
|
|
29
|
+
function parametersFor(args) {
|
|
30
|
+
const declared = args !== null && args !== void 0 ? args : [];
|
|
31
|
+
const properties = {};
|
|
32
|
+
for (const argument of declared) {
|
|
33
|
+
properties[argument.name] = propertyFor(argument);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties,
|
|
38
|
+
required: declared
|
|
39
|
+
.filter((argument) => argument.required === true)
|
|
40
|
+
.map((argument) => argument.name),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function propertyFor(argument) {
|
|
44
|
+
var _a;
|
|
45
|
+
const scalar = Object.assign({ type: (_a = SCALAR_TYPE[argument.kind]) !== null && _a !== void 0 ? _a : 'string' }, (argument.kind === 'choice' ? { enum: argument.choices } : {}));
|
|
46
|
+
return argument.list === true
|
|
47
|
+
? { type: 'array', description: argument.description, items: scalar }
|
|
48
|
+
: Object.assign(Object.assign({}, scalar), { description: argument.description });
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=tool-definitions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-definitions.js","sourceRoot":"","sources":["../../../../../../libs/integrations/ag-ui/src/lib/tool-definitions.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,GAAqC;IACpD,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,QAAQ;CACjB,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,UAAU,OAAO,CAAC,OAAyB;;IAC/C,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,EAAE;QAChB,WAAW,EAAE,MAAA,OAAO,CAAC,WAAW,mCAAI,OAAO,CAAC,KAAK;QACjD,UAAU,EAAE,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC;KAC7C,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,QAAQ,CAAC,QAAqC;IAC5D,OAAO,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,aAAa,CACpB,IAA4C;IAE5C,MAAM,QAAQ,GAAG,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAuC,EAAE,CAAC;IAC1D,KAAK,MAAM,QAAQ,IAAI,QAAQ,EAAE,CAAC;QAChC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACpD,CAAC;IACD,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,UAAU;QACV,QAAQ,EAAE,QAAQ;aACf,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC;aAChD,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC;KACpC,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,QAAyB;;IAC5C,MAAM,MAAM,mBACV,IAAI,EAAE,MAAA,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,mCAAI,QAAQ,IACzC,CAAC,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAClE,CAAC;IACF,OAAO,QAAQ,CAAC,IAAI,KAAK,IAAI;QAC3B,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE;QACrE,CAAC,iCAAM,MAAM,KAAE,WAAW,EAAE,QAAQ,CAAC,WAAW,GAAE,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ToolMessage } from '@ag-ui/core';
|
|
2
|
+
import type { CommandOutcome } from '@loomweaver/plugin-sdk';
|
|
3
|
+
/**
|
|
4
|
+
* Turns what the workbench answered into the message that goes back to the agent.
|
|
5
|
+
*
|
|
6
|
+
* A refusal and a failure both land in `error`, because the protocol has one field for "this did not
|
|
7
|
+
* give you an answer" and its own reason for having it: without it, a tool that failed cannot be told
|
|
8
|
+
* from one that succeeded. The wording keeps the distinction the workbench preserved, so an agent
|
|
9
|
+
* reading it can tell "it did not run" from "it ran and broke" and choose differently.
|
|
10
|
+
*
|
|
11
|
+
* A command that declares no answer reports plainly that it ran. An empty string would read to an
|
|
12
|
+
* agent like a tool that returned nothing useful, and the usual response to that is to try again.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resultFor(toolCallId: string, outcome: CommandOutcome): ToolMessage;
|
|
15
|
+
/** The message that answers a call the adapter could not put to the workbench at all. */
|
|
16
|
+
export declare function refusalFor(toolCallId: string, reason: string): ToolMessage;
|
|
17
|
+
/** The message that answers a call something in front of the workbench answered itself. */
|
|
18
|
+
export declare function answerFor(toolCallId: string, content: string): ToolMessage;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const RAN = 'The command ran.';
|
|
2
|
+
/**
|
|
3
|
+
* Turns what the workbench answered into the message that goes back to the agent.
|
|
4
|
+
*
|
|
5
|
+
* A refusal and a failure both land in `error`, because the protocol has one field for "this did not
|
|
6
|
+
* give you an answer" and its own reason for having it: without it, a tool that failed cannot be told
|
|
7
|
+
* from one that succeeded. The wording keeps the distinction the workbench preserved, so an agent
|
|
8
|
+
* reading it can tell "it did not run" from "it ran and broke" and choose differently.
|
|
9
|
+
*
|
|
10
|
+
* A command that declares no answer reports plainly that it ran. An empty string would read to an
|
|
11
|
+
* agent like a tool that returned nothing useful, and the usual response to that is to try again.
|
|
12
|
+
*/
|
|
13
|
+
export function resultFor(toolCallId, outcome) {
|
|
14
|
+
if (outcome.outcome === 'answered') {
|
|
15
|
+
return message(toolCallId, contentOf(outcome.value));
|
|
16
|
+
}
|
|
17
|
+
if (outcome.outcome === 'refused') {
|
|
18
|
+
return message(toolCallId, '', `The command did not run: ${outcome.message}`);
|
|
19
|
+
}
|
|
20
|
+
return message(toolCallId, '', `The command ran and failed: ${outcome.message}`);
|
|
21
|
+
}
|
|
22
|
+
/** The message that answers a call the adapter could not put to the workbench at all. */
|
|
23
|
+
export function refusalFor(toolCallId, reason) {
|
|
24
|
+
return message(toolCallId, '', `The command did not run: ${reason}`);
|
|
25
|
+
}
|
|
26
|
+
/** The message that answers a call something in front of the workbench answered itself. */
|
|
27
|
+
export function answerFor(toolCallId, content) {
|
|
28
|
+
return message(toolCallId, content);
|
|
29
|
+
}
|
|
30
|
+
function contentOf(value) {
|
|
31
|
+
if (value === undefined) {
|
|
32
|
+
return RAN;
|
|
33
|
+
}
|
|
34
|
+
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
35
|
+
}
|
|
36
|
+
function message(toolCallId, content, error) {
|
|
37
|
+
return Object.assign({ id: `tool-${toolCallId}`, role: 'tool', toolCallId,
|
|
38
|
+
content }, (error === undefined ? {} : { error }));
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=tool-results.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-results.js","sourceRoot":"","sources":["../../../../../../libs/integrations/ag-ui/src/lib/tool-results.ts"],"names":[],"mappings":"AAGA,MAAM,GAAG,GAAG,kBAAkB,CAAC;AAE/B;;;;;;;;;;GAUG;AACH,MAAM,UAAU,SAAS,CACvB,UAAkB,EAClB,OAAuB;IAEvB,IAAI,OAAO,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QACnC,OAAO,OAAO,CAAC,UAAU,EAAE,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,OAAO,CACZ,UAAU,EACV,EAAE,EACF,4BAA4B,OAAO,CAAC,OAAO,EAAE,CAC9C,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC,UAAU,EAAE,EAAE,EAAE,+BAA+B,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;AACnF,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,UAAU,CAAC,UAAkB,EAAE,MAAc;IAC3D,OAAO,OAAO,CAAC,UAAU,EAAE,EAAE,EAAE,4BAA4B,MAAM,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,SAAS,CAAC,UAAkB,EAAE,OAAe;IAC3D,OAAO,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,OAAO,CACd,UAAkB,EAClB,OAAe,EACf,KAAc;IAEd,uBACE,EAAE,EAAE,QAAQ,UAAU,EAAE,EACxB,IAAI,EAAE,MAAM,EACZ,UAAU;QACV,OAAO,IACJ,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EACzC;AACJ,CAAC"}
|