plugin-agent 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,11 @@
1
+ import type { UIMessageChunk } from "ai";
2
+ import type { FsAgentPlugin } from "./plugin";
3
+ export declare function extractXmlPlugin({ tagName, system, onTagFinish, }: {
4
+ tagName: string;
5
+ system: string;
6
+ onTagFinish: (params: {
7
+ content: string;
8
+ controller: TransformStreamDefaultController<UIMessageChunk>;
9
+ }) => void | Promise<void>;
10
+ }): FsAgentPlugin;
11
+ //# sourceMappingURL=extract-xml-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-xml-plugin.d.ts","sourceRoot":"","sources":["../src/extract-xml-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,IAAI,CAAC;AACzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,wBAAgB,gBAAgB,CAAC,EAC7B,OAAO,EACP,MAAM,EACN,WAAW,GACd,EAAE;IACC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,CAAC,MAAM,EAAE;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,gCAAgC,CAAC,cAAc,CAAC,CAAA;KAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClI,GAAG,aAAa,CAiHhB"}
@@ -0,0 +1,104 @@
1
+ export function extractXmlPlugin({ tagName, system, onTagFinish, }) {
2
+ return {
3
+ name: "extract-xml",
4
+ systemPrompt: () => system,
5
+ uiMessageTransform: () => {
6
+ const openingTag = `<${tagName}>`;
7
+ const closingTag = `<\/${tagName}>`;
8
+ const tagExtractions = {};
9
+ const transformChunk = (chunk, controller) => {
10
+ if (chunk.type !== "text-delta") {
11
+ controller.enqueue(chunk);
12
+ return;
13
+ }
14
+ if (tagExtractions[chunk.id] == null) {
15
+ tagExtractions[chunk.id] = {
16
+ isFirstTagContents: true,
17
+ isFirstText: true,
18
+ afterSwitch: false,
19
+ isTagContents: false,
20
+ buffer: "",
21
+ tagContents: "",
22
+ idCounter: 0,
23
+ textId: chunk.id,
24
+ };
25
+ }
26
+ const activeExtraction = tagExtractions[chunk.id];
27
+ activeExtraction.buffer += chunk.delta;
28
+ function publish(text) {
29
+ if (text.length > 0) {
30
+ if (activeExtraction.isTagContents) {
31
+ activeExtraction.tagContents += text;
32
+ }
33
+ else {
34
+ controller.enqueue({
35
+ type: "text-delta",
36
+ delta: text,
37
+ id: activeExtraction.textId,
38
+ });
39
+ }
40
+ activeExtraction.afterSwitch = false;
41
+ if (activeExtraction.isTagContents) {
42
+ activeExtraction.isFirstTagContents = false;
43
+ }
44
+ else {
45
+ activeExtraction.isFirstText = false;
46
+ }
47
+ }
48
+ }
49
+ do {
50
+ const nextTag = activeExtraction.isTagContents
51
+ ? closingTag
52
+ : openingTag;
53
+ const startIndex = getPotentialStartIndex(activeExtraction.buffer, nextTag);
54
+ // no opening or closing tag found, publish the buffer
55
+ if (startIndex == null) {
56
+ publish(activeExtraction.buffer);
57
+ activeExtraction.buffer = "";
58
+ break;
59
+ }
60
+ // publish text before the tag
61
+ publish(activeExtraction.buffer.slice(0, startIndex));
62
+ const foundFullMatch = startIndex + nextTag.length <= activeExtraction.buffer.length;
63
+ if (foundFullMatch) {
64
+ activeExtraction.buffer = activeExtraction.buffer.slice(startIndex + nextTag.length);
65
+ // tag part finished:
66
+ if (activeExtraction.isTagContents) {
67
+ onTagFinish({ content: activeExtraction.tagContents, controller });
68
+ }
69
+ activeExtraction.isTagContents = !activeExtraction.isTagContents;
70
+ activeExtraction.afterSwitch = true;
71
+ }
72
+ else {
73
+ activeExtraction.buffer = activeExtraction.buffer.slice(startIndex);
74
+ break;
75
+ }
76
+ } while (true);
77
+ };
78
+ return new TransformStream({
79
+ transform: transformChunk,
80
+ });
81
+ },
82
+ };
83
+ }
84
+ function getPotentialStartIndex(text, searchedText) {
85
+ // Return null immediately if searchedText is empty.
86
+ if (searchedText.length === 0) {
87
+ return null;
88
+ }
89
+ // Check if the searchedText exists as a direct substring of text.
90
+ const directIndex = text.indexOf(searchedText);
91
+ if (directIndex !== -1) {
92
+ return directIndex;
93
+ }
94
+ // Otherwise, look for the largest suffix of "text" that matches
95
+ // a prefix of "searchedText". We go from the end of text inward.
96
+ for (let i = text.length - 1; i >= 0; i--) {
97
+ const suffix = text.substring(i);
98
+ if (searchedText.startsWith(suffix)) {
99
+ return i;
100
+ }
101
+ }
102
+ return null;
103
+ }
104
+ //# sourceMappingURL=extract-xml-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-xml-plugin.js","sourceRoot":"","sources":["../src/extract-xml-plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { type LanguageModel, type UIMessage, type UIMessageChunk } from "ai";
2
+ import type { FsAgentPlugin } from "./plugin";
3
+ export type VirtualFileSystem = Record<string, string>;
4
+ export interface RunFilesystemAgentParams {
5
+ model?: LanguageModel;
6
+ system?: string;
7
+ maxSteps?: number;
8
+ tools?: Record<string, unknown>;
9
+ /** Single-turn prompt. Mutually exclusive with `newMessage` / `allMessages`. */
10
+ prompt?: string;
11
+ /**
12
+ * A single new message to append to the conversation.
13
+ * Existing history is loaded via the plugin `loadMessages` hook.
14
+ * Mutually exclusive with `prompt` / `allMessages`.
15
+ */
16
+ newMessage?: UIMessage;
17
+ /**
18
+ * Complete conversation provided by the caller (nothing loaded from storage).
19
+ * Mutually exclusive with `prompt` / `newMessage`.
20
+ */
21
+ allMessages?: UIMessage[];
22
+ }
23
+ export interface RunFilesystemAgentStreamResult {
24
+ /** Raw UI message stream to pipe to the response. Do not consume elsewhere. */
25
+ stream: ReadableStream<UIMessageChunk>;
26
+ /** Resolves when the run is complete (stream is fully consumed). */
27
+ completion: Promise<{
28
+ text: string;
29
+ response: unknown;
30
+ messages: unknown[];
31
+ usage: unknown;
32
+ providerMetadata: unknown;
33
+ }>;
34
+ }
35
+ export interface FilesystemAgentHarnessOptions {
36
+ model?: LanguageModel;
37
+ system?: string;
38
+ maxSteps?: number;
39
+ tools?: Record<string, unknown>;
40
+ plugins?: FsAgentPlugin[];
41
+ }
42
+ export declare function createFilesystemAgentHarness(options?: FilesystemAgentHarnessOptions): ({ prompt, newMessage, allMessages, model, system, maxSteps, tools, }: RunFilesystemAgentParams) => Promise<RunFilesystemAgentStreamResult>;
43
+ export declare const runFilesystemAgent: ({ prompt, newMessage, allMessages, model, system, maxSteps, tools, }: RunFilesystemAgentParams) => Promise<RunFilesystemAgentStreamResult>;
44
+ //# sourceMappingURL=harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,aAAa,EAIlB,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,IAAI,CAAC;AASZ,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEvD,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,SAAS,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,8BAA8B;IAC7C,+EAA+E;IAC/E,MAAM,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC;IACvC,oEAAoE;IACpE,UAAU,EAAE,OAAO,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,OAAO,CAAC;QAClB,QAAQ,EAAE,OAAO,EAAE,CAAC;QACpB,KAAK,EAAE,OAAO,CAAC;QACf,gBAAgB,EAAE,OAAO,CAAC;KAC3B,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,6BAA6B;IAC5C,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;CAC3B;AAKD,wBAAgB,4BAA4B,CAC1C,OAAO,GAAE,6BAAkC,IAEF,sEAQtC,wBAAwB,KAAG,OAAO,CAAC,8BAA8B,CAAC,CAiLtE;AAED,eAAO,MAAM,kBAAkB,yEAnL1B,wBAAwB,KAAG,OAAO,CAAC,8BAA8B,CAmLN,CAAC"}
@@ -0,0 +1,156 @@
1
+ import { convertToModelMessages, gateway, generateId, stepCountIs, streamText, } from "ai";
2
+ import { createBashTool } from "bash-tool";
3
+ import { Bash, InMemoryFs, MountableFs, } from "just-bash";
4
+ const DEFAULT_MODEL = "openai/gpt-5.2";
5
+ const DEFAULT_MAX_STEPS = 20;
6
+ export function createFilesystemAgentHarness(options = {}) {
7
+ return async function runFilesystemAgent({ prompt, newMessage, allMessages, model, system, maxSteps, tools, }) {
8
+ const plugins = options.plugins ?? [];
9
+ const allCommands = [];
10
+ for (const plugin of plugins) {
11
+ if (plugin.registerCommands) {
12
+ const cmds = await plugin.registerCommands();
13
+ allCommands.push(...cmds);
14
+ }
15
+ }
16
+ const mountResults = await Promise.all(plugins.map((p) => p.createFilesystem ? p.createFilesystem() : Promise.resolve(undefined)));
17
+ const mounts = mountResults.filter((m) => m !== undefined);
18
+ const fileSystem = new MountableFs({
19
+ base: new InMemoryFs(),
20
+ mounts,
21
+ });
22
+ const workspaceCwd = "/workspace";
23
+ const bash = new Bash({
24
+ cwd: workspaceCwd,
25
+ fs: fileSystem,
26
+ customCommands: allCommands.length > 0 ? allCommands : undefined,
27
+ });
28
+ const bashToolkit = await createBashTool({
29
+ sandbox: bash,
30
+ });
31
+ const pluginToolLayers = await Promise.all(plugins.map((p) => p.registerTools?.() ?? Promise.resolve({})));
32
+ const pluginTools = Object.assign({}, ...pluginToolLayers);
33
+ // Resolve input UIMessages and convert to ModelMessages
34
+ let inputUIMessages;
35
+ let modelMessages;
36
+ if (newMessage) {
37
+ const loader = [...plugins].reverse().find((p) => p.loadMessages);
38
+ const existing = loader?.loadMessages ? await loader.loadMessages() : [];
39
+ inputUIMessages = [...existing, newMessage];
40
+ modelMessages = await convertToModelMessages(inputUIMessages);
41
+ }
42
+ else if (allMessages?.length) {
43
+ inputUIMessages = allMessages;
44
+ modelMessages = await convertToModelMessages(inputUIMessages);
45
+ }
46
+ let transformedInput = {
47
+ prompt,
48
+ messages: modelMessages,
49
+ };
50
+ for (const plugin of plugins) {
51
+ if (plugin.prepareInputMessage) {
52
+ transformedInput = await plugin.prepareInputMessage(transformedInput);
53
+ }
54
+ }
55
+ let stepNumber = 0;
56
+ for (const plugin of plugins) {
57
+ if (plugin.prepareStep)
58
+ await plugin.prepareStep({ stepNumber });
59
+ }
60
+ const pluginPrompts = await Promise.all(plugins.map((p) => p.systemPrompt?.() ?? Promise.resolve("")));
61
+ const systemPrompt = `${system ?? options.system}\n${pluginPrompts.join("\n")}`;
62
+ const resolvedModel = model ?? options.model ?? DEFAULT_MODEL;
63
+ const common = {
64
+ model: typeof resolvedModel === "string"
65
+ ? gateway(resolvedModel)
66
+ : resolvedModel,
67
+ system: systemPrompt,
68
+ stopWhen: [
69
+ stepCountIs(maxSteps ?? options.maxSteps ?? DEFAULT_MAX_STEPS),
70
+ ],
71
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- plugin interface provides proper StepResult<ToolSet> typing
72
+ onStepFinish: async (step) => {
73
+ for (const plugin of plugins) {
74
+ if (plugin.onStepFinish)
75
+ await plugin.onStepFinish(step);
76
+ }
77
+ stepNumber++;
78
+ for (const plugin of plugins) {
79
+ if (plugin.prepareStep)
80
+ await plugin.prepareStep({ stepNumber });
81
+ }
82
+ },
83
+ tools: {
84
+ ...bashToolkit.tools,
85
+ ...pluginTools,
86
+ ...(options.tools ?? {}),
87
+ ...(tools ?? {}),
88
+ },
89
+ };
90
+ const streamedResult = transformedInput.messages?.length
91
+ ? streamText({ ...common, messages: transformedInput.messages })
92
+ : streamText({ ...common, prompt: transformedInput.prompt ?? "" });
93
+ const hasPluginFinish = plugins.some((p) => p.onFinish);
94
+ const uiStream = streamedResult.toUIMessageStream({
95
+ ...(hasPluginFinish
96
+ ? {
97
+ generateMessageId: generateId,
98
+ onFinish: async ({ messages: streamMessages }) => {
99
+ // Merge input UIMessages with stream output for the full conversation
100
+ let merged;
101
+ if (inputUIMessages?.length) {
102
+ merged = [...inputUIMessages];
103
+ for (const msg of streamMessages) {
104
+ const idx = merged.findIndex((m) => m.id === msg.id);
105
+ if (idx >= 0)
106
+ merged[idx] = msg;
107
+ else
108
+ merged.push(msg);
109
+ }
110
+ }
111
+ else {
112
+ merged = streamMessages;
113
+ }
114
+ for (const plugin of plugins) {
115
+ if (plugin.onFinish) {
116
+ try {
117
+ await plugin.onFinish({ messages: merged, fileSystem });
118
+ }
119
+ catch (err) {
120
+ console.warn(`[fs-agent] Plugin ${plugin.name} onFinish error:`, err);
121
+ }
122
+ }
123
+ }
124
+ },
125
+ }
126
+ : {}),
127
+ });
128
+ let transformedUiMessages = uiStream;
129
+ for (const plugin of plugins) {
130
+ if (plugin.uiMessageTransform) {
131
+ transformedUiMessages = transformedUiMessages.pipeThrough(await plugin.uiMessageTransform());
132
+ }
133
+ }
134
+ const completion = (async () => {
135
+ const [text, response, usage, providerMetadata] = await Promise.all([
136
+ streamedResult.text,
137
+ streamedResult.response,
138
+ streamedResult.usage,
139
+ streamedResult.providerMetadata,
140
+ ]);
141
+ return {
142
+ text,
143
+ response,
144
+ messages: response.messages,
145
+ usage,
146
+ providerMetadata,
147
+ };
148
+ })();
149
+ return {
150
+ stream: transformedUiMessages,
151
+ completion,
152
+ };
153
+ };
154
+ }
155
+ export const runFilesystemAgent = createFilesystemAgentHarness();
156
+ //# sourceMappingURL=harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.js","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,OAAO,EACP,UAAU,EAGV,WAAW,EACX,UAAU,GAGX,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EACL,IAAI,EAGJ,UAAU,EACV,WAAW,GACZ,MAAM,WAAW,CAAC;AA8CnB,MAAM,aAAa,GAAG,gBAAgB,CAAC;AACvC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAE7B,MAAM,UAAU,4BAA4B,CAC1C,UAAyC,EAAE;IAE3C,OAAO,KAAK,UAAU,kBAAkB,CAAC,EACvC,MAAM,EACN,UAAU,EACV,WAAW,EACX,KAAK,EACL,MAAM,EACN,QAAQ,EACR,KAAK,GACoB;QACzB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;QAEtC,MAAM,WAAW,GAAoB,EAAE,CAAC;QACxC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;gBAC5B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,gBAAgB,EAAE,CAAC;gBAC7C,WAAW,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,OAAO,CAAC,GAAG,CACpC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAChB,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CACvE,CACF,CAAC;QACF,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAChC,CACE,CAAC,EACqD,EAAE,CAAC,CAAC,KAAK,SAAS,CAC3E,CAAC;QAEF,MAAM,UAAU,GAAG,IAAI,WAAW,CAAC;YACjC,IAAI,EAAE,IAAI,UAAU,EAAE;YACtB,MAAM;SACP,CAAC,CAAC;QAEH,MAAM,YAAY,GAAG,YAAY,CAAC;QAElC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC;YACpB,GAAG,EAAE,YAAY;YACjB,EAAE,EAAE,UAAU;YACd,cAAc,EAAE,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;SACjE,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,MAAM,cAAc,CAAC;YACvC,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QAEH,MAAM,gBAAgB,GAAG,MAAM,OAAO,CAAC,GAAG,CACxC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAC/D,CAAC;QACF,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,gBAAgB,CAAC,CAAC;QAE3D,wDAAwD;QACxD,IAAI,eAAwC,CAAC;QAC7C,IAAI,aAAyC,CAAC;QAE9C,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;YAClE,MAAM,QAAQ,GAAG,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,eAAe,GAAG,CAAC,GAAG,QAAQ,EAAE,UAAU,CAAC,CAAC;YAC5C,aAAa,GAAG,MAAM,sBAAsB,CAAC,eAAe,CAAC,CAAC;QAChE,CAAC;aAAM,IAAI,WAAW,EAAE,MAAM,EAAE,CAAC;YAC/B,eAAe,GAAG,WAAW,CAAC;YAC9B,aAAa,GAAG,MAAM,sBAAsB,CAAC,eAAe,CAAC,CAAC;QAChE,CAAC;QAED,IAAI,gBAAgB,GAAmD;YACrE,MAAM;YACN,QAAQ,EAAE,aAAa;SACxB,CAAC;QACF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,mBAAmB,EAAE,CAAC;gBAC/B,gBAAgB,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;QAED,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,WAAW;gBAAE,MAAM,MAAM,CAAC,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,CACrC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAC9D,CAAC;QACF,MAAM,YAAY,GAAG,GAAG,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAEhF,MAAM,aAAa,GAAG,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI,aAAa,CAAC;QAC9D,MAAM,MAAM,GAAG;YACb,KAAK,EACH,OAAO,aAAa,KAAK,QAAQ;gBAC/B,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;gBACxB,CAAC,CAAC,aAAa;YACnB,MAAM,EAAE,YAAY;YACpB,QAAQ,EAAE;gBACR,WAAW,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC;aAC/D;YACD,6HAA6H;YAC7H,YAAY,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;gBAChC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;oBAC7B,IAAI,MAAM,CAAC,YAAY;wBAAE,MAAM,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBAC3D,CAAC;gBACD,UAAU,EAAE,CAAC;gBACb,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;oBAC7B,IAAI,MAAM,CAAC,WAAW;wBAAE,MAAM,MAAM,CAAC,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;gBACnE,CAAC;YACH,CAAC;YACD,KAAK,EAAE;gBACL,GAAG,WAAW,CAAC,KAAK;gBACpB,GAAG,WAAW;gBACd,GAAG,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;gBACxB,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;aACjB;SACF,CAAC;QAEF,MAAM,cAAc,GAAG,gBAAgB,CAAC,QAAQ,EAAE,MAAM;YACtD,CAAC,CAAC,UAAU,CAAC,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,gBAAgB,CAAC,QAAQ,EAAE,CAAC;YAChE,CAAC,CAAC,UAAU,CAAC,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,gBAAgB,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC;QAErE,MAAM,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QAExD,MAAM,QAAQ,GAAG,cAAc,CAAC,iBAAiB,CAAC;YAChD,GAAG,CAAC,eAAe;gBACjB,CAAC,CAAC;oBACE,iBAAiB,EAAE,UAAU;oBAC7B,QAAQ,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,EAAE,EAAE;wBAC/C,sEAAsE;wBACtE,IAAI,MAAmB,CAAC;wBACxB,IAAI,eAAe,EAAE,MAAM,EAAE,CAAC;4BAC5B,MAAM,GAAG,CAAC,GAAG,eAAe,CAAC,CAAC;4BAC9B,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;gCACjC,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC;gCACrD,IAAI,GAAG,IAAI,CAAC;oCAAE,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;;oCAC3B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;4BACxB,CAAC;wBACH,CAAC;6BAAM,CAAC;4BACN,MAAM,GAAG,cAAc,CAAC;wBAC1B,CAAC;wBAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;4BAC7B,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gCACpB,IAAI,CAAC;oCACH,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;gCAC1D,CAAC;gCAAC,OAAO,GAAG,EAAE,CAAC;oCACb,OAAO,CAAC,IAAI,CACV,qBAAqB,MAAM,CAAC,IAAI,kBAAkB,EAClD,GAAG,CACJ,CAAC;gCACJ,CAAC;4BACH,CAAC;wBACH,CAAC;oBACH,CAAC;iBACF;gBACH,CAAC,CAAC,EAAE,CAAC;SACR,CAAC,CAAC;QAEH,IAAI,qBAAqB,GAAG,QAAQ,CAAC;QACrC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,kBAAkB,EAAE,CAAC;gBAC9B,qBAAqB,GAAG,qBAAqB,CAAC,WAAW,CACvD,MAAM,MAAM,CAAC,kBAAkB,EAAE,CAClC,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,UAAU,GAAG,CAAC,KAAK,IAAI,EAAE;YAC7B,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAClE,cAAc,CAAC,IAAI;gBACnB,cAAc,CAAC,QAAQ;gBACvB,cAAc,CAAC,KAAK;gBACpB,cAAc,CAAC,gBAAgB;aAChC,CAAC,CAAC;YACH,OAAO;gBACL,IAAI;gBACJ,QAAQ;gBACR,QAAQ,EAAE,QAAQ,CAAC,QAAQ;gBAC3B,KAAK;gBACL,gBAAgB;aACjB,CAAC;QACJ,CAAC,CAAC,EAAE,CAAC;QAEL,OAAO;YACL,MAAM,EAAE,qBAAqB;YAC7B,UAAU;SACX,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,4BAA4B,EAAE,CAAC"}
@@ -0,0 +1,5 @@
1
+ export type { FsAgentPlugin } from "./plugin";
2
+ export { extractXmlPlugin } from "./extract-xml-plugin";
3
+ export { createSkillsPlugin, type AgentSkill, } from "./skills-plugin";
4
+ export { createFilesystemAgentHarness, runFilesystemAgent, } from "./harness";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EACL,kBAAkB,EAClB,KAAK,UAAU,GAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { extractXmlPlugin } from "./extract-xml-plugin";
2
+ export { createSkillsPlugin, } from "./skills-plugin";
3
+ export { createFilesystemAgentHarness, runFilesystemAgent, } from "./harness";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EACL,kBAAkB,GAEnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,4BAA4B,EAC5B,kBAAkB,GACnB,MAAM,WAAW,CAAC"}
@@ -0,0 +1,81 @@
1
+ import type { CustomCommand, IFileSystem } from "just-bash";
2
+ import type { ModelMessage, StepResult, ToolSet, UIMessage, UIMessageChunk } from "ai";
3
+ /**
4
+ * Plugin interface for extending the fs-agent harness.
5
+ *
6
+ * The harness uses just-bash / bash-tool by default; plugins hook into
7
+ * the agent lifecycle to add commands, wrap the filesystem, transform
8
+ * input, and react to step / finish events.
9
+ */
10
+ export interface FsAgentPlugin {
11
+ name: string;
12
+ /**
13
+ * Text to include in the system prompt
14
+ */
15
+ systemPrompt?(): string | Promise<string>;
16
+ /**
17
+ * Tools to merge into the harness (after bash tools, before `options.tools`
18
+ * and per-run `tools`). Later plugins override earlier keys on collision.
19
+ */
20
+ registerTools?(): ToolSet | Promise<ToolSet>;
21
+ /**
22
+ * Register custom bash commands for the agent's shell.
23
+ * Commands from all plugins are merged together.
24
+ */
25
+ registerCommands?(): CustomCommand[] | Promise<CustomCommand[]>;
26
+ /**
27
+ * Create or wrap the virtual filesystem before the bash environment starts.
28
+ * Returns the mount point (e.g. "/workspace") and the filesystem to use.
29
+ * Multiple plugins can contribute mounts; all are combined via MountableFs.
30
+ * Return undefined to skip adding a mount.
31
+ */
32
+ createFilesystem?(): {
33
+ mountPoint: string;
34
+ filesystem: IFileSystem;
35
+ } | Promise<{
36
+ mountPoint: string;
37
+ filesystem: IFileSystem;
38
+ }> | undefined;
39
+ /**
40
+ * Transform the input prompt or messages before the agent run begins.
41
+ * Plugins are called in order; each receives the previous plugin's output.
42
+ */
43
+ prepareInputMessage?(input: {
44
+ prompt?: string;
45
+ messages?: ModelMessage[];
46
+ }): {
47
+ prompt?: string;
48
+ messages?: ModelMessage[];
49
+ } | Promise<{
50
+ prompt?: string;
51
+ messages?: ModelMessage[];
52
+ }>;
53
+ /**
54
+ * Called before each LLM inference step (0-indexed).
55
+ * Useful for dynamic context injection or rate-limiting.
56
+ */
57
+ prepareStep?(context: {
58
+ stepNumber: number;
59
+ }): void | Promise<void>;
60
+ uiMessageTransform?(): TransformStream<UIMessageChunk, UIMessageChunk> | Promise<TransformStream<UIMessageChunk, UIMessageChunk>>;
61
+ /**
62
+ * Called after each LLM step completes.
63
+ */
64
+ onStepFinish?(step: StepResult<ToolSet>): void | Promise<void>;
65
+ /**
66
+ * Load existing conversation messages from storage.
67
+ * Only one plugin should implement this hook (last wins).
68
+ * Called by the harness when `newMessage` mode is used to hydrate history.
69
+ */
70
+ loadMessages?(): UIMessage[] | Promise<UIMessage[]>;
71
+ /**
72
+ * Called when the agent run is fully complete and UI messages are available.
73
+ * Receives the full merged conversation (input messages + stream output).
74
+ * Useful for persisting conversation history or post-processing.
75
+ */
76
+ onFinish?(params: {
77
+ messages: UIMessage[];
78
+ fileSystem?: IFileSystem;
79
+ }): void | Promise<void>;
80
+ }
81
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,OAAO,EACP,SAAS,EACT,cAAc,EACf,MAAM,IAAI,CAAC;AAEZ;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,YAAY,CAAC,IAAI,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE1C;;;OAGG;IACH,aAAa,CAAC,IAAI,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE7C;;;OAGG;IACH,gBAAgB,CAAC,IAAI,aAAa,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAEhE;;;;;OAKG;IACH,gBAAgB,CAAC,IACb;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,WAAW,CAAA;KAAE,GAC/C,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,WAAW,CAAA;KAAE,CAAC,GACxD,SAAS,CAAC;IAEd;;;OAGG;IACH,mBAAmB,CAAC,CAAC,KAAK,EAAE;QAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;KAC3B,GACG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAA;KAAE,GAC9C,OAAO,CAAC;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAA;KAAE,CAAC,CAAC;IAE5D;;;OAGG;IACH,WAAW,CAAC,CAAC,OAAO,EAAE;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpE,kBAAkB,CAAC,IACf,eAAe,CAAC,cAAc,EAAE,cAAc,CAAC,GAC/C,OAAO,CAAC,eAAe,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC,CAAC;IAE7D;;OAEG;IACH,YAAY,CAAC,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/D;;;;OAIG;IACH,YAAY,CAAC,IAAI,SAAS,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAEpD;;;;OAIG;IACH,QAAQ,CAAC,CAAC,MAAM,EAAE;QAChB,QAAQ,EAAE,SAAS,EAAE,CAAC;QACtB,UAAU,CAAC,EAAE,WAAW,CAAC;KAC1B,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B"}
package/dist/plugin.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":""}
@@ -0,0 +1,14 @@
1
+ import type { FsAgentPlugin } from "./plugin";
2
+ export interface AgentSkill {
3
+ name: string;
4
+ description: string;
5
+ content: string;
6
+ }
7
+ /**
8
+ * Supplies in-memory skills: names and descriptions go into the system prompt;
9
+ * full content is loaded on demand via the `read_skill` tool.
10
+ */
11
+ export declare function createSkillsPlugin(options: {
12
+ skills: AgentSkill[];
13
+ }): FsAgentPlugin;
14
+ //# sourceMappingURL=skills-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills-plugin.d.ts","sourceRoot":"","sources":["../src/skills-plugin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAkBD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB,GAAG,aAAa,CAkChB"}
@@ -0,0 +1,56 @@
1
+ import { tool } from "ai";
2
+ import { z } from "zod";
3
+ function skillsSystemPromptSection(skills) {
4
+ const lines = [
5
+ "## Agent skills",
6
+ "You have access to optional skills. Each skill's name and description are listed below. The full body of a skill is not in this message — use the read_skill tool with the exact skill name when you need it.",
7
+ "",
8
+ ];
9
+ if (skills.length === 0) {
10
+ lines.push("(No skills are configured.)");
11
+ }
12
+ else {
13
+ for (const s of skills) {
14
+ lines.push(`- **${s.name}**: ${s.description}`);
15
+ }
16
+ }
17
+ return lines.join("\n");
18
+ }
19
+ /**
20
+ * Supplies in-memory skills: names and descriptions go into the system prompt;
21
+ * full content is loaded on demand via the `read_skill` tool.
22
+ */
23
+ export function createSkillsPlugin(options) {
24
+ const { skills } = options;
25
+ const byName = new Map();
26
+ for (const s of skills) {
27
+ if (byName.has(s.name)) {
28
+ throw new Error(`createSkillsPlugin: duplicate skill name "${s.name}"`);
29
+ }
30
+ byName.set(s.name, s);
31
+ }
32
+ const readSkill = tool({
33
+ description: "Load the full text of an agent skill by name. Call this when you need to follow skill-specific instructions.",
34
+ inputSchema: z.object({
35
+ name: z
36
+ .string()
37
+ .describe("Exact skill name as shown in the system prompt."),
38
+ }),
39
+ execute: async ({ name }) => {
40
+ const skill = byName.get(name);
41
+ if (!skill) {
42
+ const available = [...byName.keys()].join(", ") || "none";
43
+ return {
44
+ error: `Unknown skill "${name}". Available: ${available}`,
45
+ };
46
+ }
47
+ return { name: skill.name, content: skill.content };
48
+ },
49
+ });
50
+ return {
51
+ name: "skills",
52
+ systemPrompt: () => skillsSystemPromptSection(skills),
53
+ registerTools: () => ({ read_skill: readSkill }),
54
+ };
55
+ }
56
+ //# sourceMappingURL=skills-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills-plugin.js","sourceRoot":"","sources":["../src/skills-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC1B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,SAAS,yBAAyB,CAAC,MAAoB;IACrD,MAAM,KAAK,GAAG;QACZ,iBAAiB;QACjB,+MAA+M;QAC/M,EAAE;KACH,CAAC;IACF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC5C,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAElC;IACC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAC3B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;QAC1E,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACxB,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC;QACrB,WAAW,EAAE,8GAA8G;QAC3H,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,QAAQ,CAAC,iDAAiD,CAAC;SAC/D,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;YAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,SAAS,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC;gBAC1D,OAAO;oBACL,KAAK,EAAE,kBAAkB,IAAI,iBAAiB,SAAS,EAAE;iBAC1D,CAAC;YACJ,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QACtD,CAAC;KACF,CAAC,CAAC;IAEH,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,GAAG,EAAE,CAAC,yBAAyB,CAAC,MAAM,CAAC;QACrD,aAAa,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;KACjD,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "plugin-agent",
3
+ "version": "0.0.1",
4
+ "description": "A filesystem-based, pluggable agent harness",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "keywords": [],
19
+ "author": "Ethan Shea",
20
+ "license": "MIT",
21
+ "dependencies": {
22
+ "ai": "^6.0.141",
23
+ "bash-tool": "1.3.15",
24
+ "just-bash": "2.11.5",
25
+ "zod": "^4.1.8"
26
+ },
27
+ "scripts": {
28
+ "build": "tsc",
29
+ "test": "echo \"Error: no test specified\" && exit 1"
30
+ }
31
+ }