@shuttle-ai/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.
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ ## `@shuttle-ai/agent`
2
+
3
+ #### 说明
4
+
5
+ 该部分是shuttle-ai的智能体操作包
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const d=require("@langchain/core/callbacks/base"),o=require("node:crypto"),a=require("../cluster/instance.cjs");class l extends d.BaseCallbackHandler{constructor(t){super(),this.options=t}name="LLMMessage";handleLLMNewToken(t,n,s){t&&this.options.agentCluster.options.hooks.onChunk?.({role:"assistant_chunk",content:t,id:s,agentId:this.options.agentId,workId:this.options.agentCluster.id})}handleLLMEnd(t,n){const s=t.generations[0][0].message,i=s.tool_calls?.filter(e=>e.name!==a.default.CALL_SUB_AGENT_NAME)?.map(e=>({id:e.id||o.randomUUID(),name:e.name,args:e.args})),r=s.tool_calls?.filter(e=>e.name===a.default.CALL_SUB_AGENT_NAME)?.map(e=>({id:e.id||o.randomUUID(),name:e.args.subAgentName}));this.options.agentCluster.addMessage({role:"assistant",content:s.content,toolCalls:i,id:n,subAgents:r,agentId:this.options.agentId,workId:this.options.agentCluster.id})}}exports.default=l;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const r=require("node:path"),i=require("node:fs/promises");class g{constructor(s){this.dirPath=s}async saveMessage(s){const t=await this.getMessagesByAgentId(s.workId,s.agentId),e=r.join(this.dirPath,`${s.workId}`,`${s.agentId}.json`);t.push(s),await i.mkdir(r.dirname(e),{recursive:!0}),await i.writeFile(e,JSON.stringify(t,null,2))}async getMessagesByAgentId(s,t){const e=r.join(this.dirPath,`${s}`,`${t}.json`);try{const a=await i.readFile(e,"utf8");return JSON.parse(a)}catch{return[]}}async getSubAgentTasks(s,t){const e=[];for(const a of t){const n=(await this.getMessagesByAgentId(s,a)).find(c=>c.role==="user");n&&e.push(n)}return e}}exports.default=g;
@@ -0,0 +1,5 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const _=require("@langchain/core/runnables"),u=require("langchain"),l=require("@langchain/core/messages"),d=require("@langchain/core/tools"),c=require("zod"),g=require("node:crypto"),p=require("../middleware/dynamicToolInterceptor.cjs"),h=require("../callback/LLMMessage.cjs");class i extends _.Runnable{constructor(t){super(),this.options=t,this.id=t.id||g.randomUUID(),t.single?.addEventListener("abort",()=>{this.stop()})}static MAIN_AGENT_NAME="main_agent";static CALL_SUB_AGENT_NAME="call_sub_agent";lc_namespace=["shuttle-ai","agent","cluster"];id;messages=[];abortController=new AbortController;addMessage(t){this.messages.push(t),this.options.messageCollector?.saveMessage(t)}async invoke(t,s){const e=await this.options.hooks.onAgentStart({agentId:this.id,agentName:i.MAIN_AGENT_NAME,content:t}),a=this.createAgent(e,this.id);this.addMessage({id:g.randomUUID(),workId:this.id,agentId:this.id,role:"user",content:t});const r=await this.revokeMessages(this.id),o=a.streamEvents({messages:[...r,{role:"user",content:t}]},{signal:this.abortController.signal,context:{...s?.context||{},_agentCluster:this,_agentId:this.id},callbacks:[new h.default({agentCluster:this,agentId:this.id})]});return await Array.fromAsync(o),this.options.hooks.onAgentEnd?.(this.id),this.getLastAiMessage(this.id)?.content||""}stop(t="stop"){this.abortController.abort(t)}createAgent({tools:t,subAgents:s,middleware:e,...a},r){let o=[];return t?.length&&(o=this.normalizationTools(t)),s?.length&&o.push(this.subAgentToDynamicTool(s,r)),u.createAgent({...a,middleware:[...e||[],p.default,u.todoListMiddleware()],tools:o})}normalizationTools(t){return t.map(s=>"lc_namespace"in s||"lc_name"in s?s:this.customToolToDynamicTool(s))}customToolToDynamicTool(t){return d.tool(s=>"",t)}subAgentToDynamicTool(t,s){return d.tool(async(e,a)=>{if(!t.find(b=>b.name===e.subAgentName))throw new Error(`Sub-agent ${e.subAgentName} not found.`);const o=this.getLastAiMessage(s),n=a.toolCall.id,m=await this.options.hooks.onAgentStart({agentId:n,agentName:e.subAgentName,beloneMessageId:o?.id,parentAgentId:s,content:e.request}),A=this.createAgent(m,n);this.addMessage({id:g.randomUUID(),workId:this.id,agentId:n,role:"user",content:e.request});const M=A.streamEvents({messages:[{role:"user",content:e.request}]},{signal:this.abortController.signal,context:{...a.context||{},_agentCluster:this,_agentId:n,_parentAgentId:s},callbacks:[new h.default({agentCluster:this,agentId:n})]});return await Array.fromAsync(M),this.options.hooks.onAgentEnd?.(n),this.getLastAiMessage(n)?.content||""},{name:i.CALL_SUB_AGENT_NAME,description:`Call a sub-agent to handle the user request.
2
+ The sub-agents are:
3
+ ${t.map(e=>`- ${e.name}: ${e.description}`).join(`
4
+ `)}
5
+ `,schema:c.z.object({subAgentName:c.z.literal(t.map(e=>e.name)).describe("The name of the sub-agent to call."),request:c.z.string().describe("The request to send to the sub-agent.")}),extras:{scope:"autoRun",skipReport:!0}})}getLastAiMessage(t){for(let s=this.messages.length-1;s>=0;s--){const e=this.messages[s];if(e.agentId===t&&e.role==="assistant")return e}}getMessages(){return this.messages}async revokeMessages(t){return this.options.messageCollector?(await this.options.messageCollector.getMessagesByAgentId(this.id,t)).map(e=>e.role==="user"?new l.HumanMessage(e.content):e.role==="assistant"?new l.AIMessage({content:e.content,tool_calls:e.toolCalls}):new l.ToolMessage({content:e.content||e.confirm?.result,tool_call_id:e.id})):[]}}exports.default=i;
@@ -0,0 +1,4 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const A=require("node:stream");function E(i,n){const r={},s={},c=new A.Readable({read(){}});let d=Date.now();function o(e){c.push(`${JSON.stringify(e)}
2
+
3
+ `),d=Date.now()}const v=setInterval(()=>{Date.now()-d>20*1e3&&o({type:"ping",data:void 0})},30*1e3);function p(){c.push(null),clearInterval(v)}function y(e,t){if(!s[e])throw new Error(`not find confirm tool resolver: ${e}`);s[e](t),delete s[e]}function w(e,t){if(!r[e])throw new Error(`not find init agent resolver: ${e}`);r[e](t),delete r[e]}return{hooks:{onChunk(e){a("onChunk",[e],n),o({type:"chunk",data:{chunk:e}})},async onAgentStart(e){a("onAgentStart",[e],n),o({type:"agentStart",data:e});const{promise:t,resolve:m}=Promise.withResolvers();r[e.agentId]=m;const u=await t,l=await i(e.agentName),f=[l?.systemPrompt||"",u.systemPrompt||""].filter(Boolean),S=(u.tools||[]).map(g=>({...g,extras:{...g.extras,remote:!0}}));return{...l,systemPrompt:f.length>0?f.join(`
4
+ `):void 0,tools:[...l?.tools||[],...S],subAgents:[...l?.subAgents||[],...u.subAgents||[]]}},onAgentEnd(e){a("onAgentEnd",[e],n),o({type:"agentEnd",data:{agentId:e}})},async onToolStart(e){if(a("onToolStart",[e],n),o({type:"toolStart",data:{tool:e}}),e.needConfirm){const{resolve:t,promise:m}=Promise.withResolvers();return s[e.toolCall.id]=t,m}return{type:"confirm"}},onToolEnd(e,t){a("onToolEnd",[e,t],n),o({type:"toolEnd",data:{toolPath:e,toolResult:t}})}},stream:c,send:o,close:p,resolveConfirmTool:y,resolveAgentStart:w}}function a(i,n,r){r?.forEach(s=>s[i]?.(...n))}exports.default=E;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./cluster/instance.cjs"),r=require("./cluster/readableHook.cjs"),t=require("./cluster/fileMessageCollector.cjs"),o=require("./callback/LLMMessage.cjs"),l=require("./middleware/dynamicToolInterceptor.cjs");require("@shuttle-ai/type");exports.AgentCluster=e.default;exports.readableHook=r.default;exports.FileMessageCollector=t.default;exports.LLMMessage=o.default;exports.dynamicToolInterceptorMiddleware=l.default;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const C=require("langchain"),u=require("@langchain/core/tools"),s=require("@langchain/core/messages"),M=require("../cluster/instance.cjs"),p=C.createMiddleware({name:"dynamicToolInterceptorMiddleware",wrapToolCall:async(o,i)=>{const e=o.runtime.context,a={role:"tool",name:o.toolCall.name,id:o.toolCall.id||"",aiMessageId:"",agentId:e._agentId,workId:e._agentCluster.id};let t;if(o.tool instanceof u.DynamicTool||o.tool instanceof u.DynamicStructuredTool||o.tool&&"extras"in o.tool){const l=o.tool.extras||{};if(!l.skipReport){const f=!w(l,e._agentCluster),r=e._agentCluster.getLastAiMessage(e._agentId);a.aiMessageId=r?.id||"";const _=r?.toolCalls?.find(c=>c.id===o.toolCall.id),n=await e._agentCluster.options.hooks.onToolStart({role:"assistant_tool",toolCall:_,needConfirm:l.remote||f,id:r?.id||"",agentId:e._agentId,workId:e._agentCluster.id});if(a.confirm=n,n.type==="reject"||n.type==="confirm"&&l.remote)return e._agentCluster.addMessage(a),new s.ToolMessage({content:n.reason||"can not run tool",tool_call_id:o.toolCall.id||""});if(n.type==="confirmWithResult")return e._agentCluster.addMessage(a),new s.ToolMessage({content:typeof n.result=="object"?JSON.stringify(n.result):n.result||"success",tool_call_id:o.toolCall.id||""});const d={agentId:e._agentId,messageId:r?.id||"",toolId:o.toolCall.id||""};if(n.newArgs){o.toolCall.args=n.newArgs;const g=o.state.messages[o.state.messages.length-1].tool_calls?.find(m=>m.id===o.toolCall.id);g&&(g.args=n.newArgs)}try{t=await i(o),t instanceof s.ToolMessage||"content"in t?e._agentCluster.options.hooks.onToolEnd?.(d,t.content):e._agentCluster.options.hooks.onToolEnd?.(d,"{}")}catch(c){t=new s.ToolMessage({content:c.message||"can not run tool",tool_call_id:o.toolCall.id||""})}}}if(!t)try{t=await i(o)}catch(l){t=new s.ToolMessage({content:l.message||"can not run tool",tool_call_id:o.toolCall.id||""})}return"lg_name"in t&&t.lg_name==="Command"&&(t=t?.update?.messages?.[0]),t instanceof s.ToolMessage&&t.name!==M.default.CALL_SUB_AGENT_NAME&&(a.content=t.content,e._agentCluster.addMessage(a)),t}});function w(o,i){return o.scope==="autoRun"||i.options.autoRunScope==="always"||i.options.autoRunScope==="read"&&o.scope!=="write"}exports.default=p;
@@ -0,0 +1,40 @@
1
+ import { BaseCallbackHandler as d } from "@langchain/core/callbacks/base";
2
+ import { randomUUID as o } from "node:crypto";
3
+ import a from "../cluster/instance.mjs";
4
+ class u extends d {
5
+ constructor(s) {
6
+ super(), this.options = s;
7
+ }
8
+ name = "LLMMessage";
9
+ handleLLMNewToken(s, n, e) {
10
+ s && this.options.agentCluster.options.hooks.onChunk?.({
11
+ role: "assistant_chunk",
12
+ content: s,
13
+ id: e,
14
+ agentId: this.options.agentId,
15
+ workId: this.options.agentCluster.id
16
+ });
17
+ }
18
+ handleLLMEnd(s, n) {
19
+ const e = s.generations[0][0].message, i = e.tool_calls?.filter((t) => t.name !== a.CALL_SUB_AGENT_NAME)?.map((t) => ({
20
+ id: t.id || o(),
21
+ name: t.name,
22
+ args: t.args
23
+ })), r = e.tool_calls?.filter((t) => t.name === a.CALL_SUB_AGENT_NAME)?.map((t) => ({
24
+ id: t.id || o(),
25
+ name: t.args.subAgentName
26
+ }));
27
+ this.options.agentCluster.addMessage({
28
+ role: "assistant",
29
+ content: e.content,
30
+ toolCalls: i,
31
+ id: n,
32
+ subAgents: r,
33
+ agentId: this.options.agentId,
34
+ workId: this.options.agentCluster.id
35
+ });
36
+ }
37
+ }
38
+ export {
39
+ u as default
40
+ };
@@ -0,0 +1,40 @@
1
+ import r from "node:path";
2
+ import i from "node:fs/promises";
3
+ class u {
4
+ constructor(s) {
5
+ this.dirPath = s;
6
+ }
7
+ async saveMessage(s) {
8
+ const e = await this.getMessagesByAgentId(
9
+ s.workId,
10
+ s.agentId
11
+ ), t = r.join(
12
+ this.dirPath,
13
+ `${s.workId}`,
14
+ `${s.agentId}.json`
15
+ );
16
+ e.push(s), await i.mkdir(r.dirname(t), { recursive: !0 }), await i.writeFile(t, JSON.stringify(e, null, 2));
17
+ }
18
+ async getMessagesByAgentId(s, e) {
19
+ const t = r.join(this.dirPath, `${s}`, `${e}.json`);
20
+ try {
21
+ const a = await i.readFile(t, "utf8");
22
+ return JSON.parse(a);
23
+ } catch {
24
+ return [];
25
+ }
26
+ }
27
+ async getSubAgentTasks(s, e) {
28
+ const t = [];
29
+ for (const a of e) {
30
+ const n = (await this.getMessagesByAgentId(s, a)).find(
31
+ (g) => g.role === "user"
32
+ );
33
+ n && t.push(n);
34
+ }
35
+ return t;
36
+ }
37
+ }
38
+ export {
39
+ u as default
40
+ };
@@ -0,0 +1,170 @@
1
+ import { Runnable as M } from "@langchain/core/runnables";
2
+ import { createAgent as p, todoListMiddleware as b } from "langchain";
3
+ import { HumanMessage as f, AIMessage as T, ToolMessage as _ } from "@langchain/core/messages";
4
+ import { tool as c } from "@langchain/core/tools";
5
+ import { z as i } from "zod";
6
+ import { randomUUID as l } from "node:crypto";
7
+ import w from "../middleware/dynamicToolInterceptor.mjs";
8
+ import d from "../callback/LLMMessage.mjs";
9
+ class g extends M {
10
+ constructor(e) {
11
+ super(), this.options = e, this.id = e.id || l(), e.single?.addEventListener("abort", () => {
12
+ this.stop();
13
+ });
14
+ }
15
+ static MAIN_AGENT_NAME = "main_agent";
16
+ static CALL_SUB_AGENT_NAME = "call_sub_agent";
17
+ lc_namespace = ["shuttle-ai", "agent", "cluster"];
18
+ id;
19
+ messages = [];
20
+ abortController = new AbortController();
21
+ addMessage(e) {
22
+ this.messages.push(e), this.options.messageCollector?.saveMessage(e);
23
+ }
24
+ async invoke(e, s) {
25
+ const t = await this.options.hooks.onAgentStart({
26
+ agentId: this.id,
27
+ agentName: g.MAIN_AGENT_NAME,
28
+ content: e
29
+ }), a = this.createAgent(t, this.id);
30
+ this.addMessage({
31
+ id: l(),
32
+ workId: this.id,
33
+ agentId: this.id,
34
+ role: "user",
35
+ content: e
36
+ });
37
+ const r = await this.revokeMessages(this.id), o = a.streamEvents(
38
+ {
39
+ messages: [...r, { role: "user", content: e }]
40
+ },
41
+ {
42
+ signal: this.abortController.signal,
43
+ context: {
44
+ ...s?.context || {},
45
+ _agentCluster: this,
46
+ _agentId: this.id
47
+ },
48
+ callbacks: [
49
+ new d({
50
+ agentCluster: this,
51
+ agentId: this.id
52
+ })
53
+ ]
54
+ }
55
+ );
56
+ return await Array.fromAsync(o), this.options.hooks.onAgentEnd?.(this.id), this.getLastAiMessage(this.id)?.content || "";
57
+ }
58
+ stop(e = "stop") {
59
+ this.abortController.abort(e);
60
+ }
61
+ createAgent({
62
+ tools: e,
63
+ subAgents: s,
64
+ middleware: t,
65
+ ...a
66
+ }, r) {
67
+ let o = [];
68
+ return e?.length && (o = this.normalizationTools(e)), s?.length && o.push(this.subAgentToDynamicTool(s, r)), p({
69
+ ...a,
70
+ middleware: [
71
+ ...t || [],
72
+ w,
73
+ b()
74
+ ],
75
+ tools: o
76
+ });
77
+ }
78
+ normalizationTools(e) {
79
+ return e.map((s) => "lc_namespace" in s || "lc_name" in s ? s : this.customToolToDynamicTool(s));
80
+ }
81
+ customToolToDynamicTool(e) {
82
+ return c((s) => "", e);
83
+ }
84
+ subAgentToDynamicTool(e, s) {
85
+ return c(
86
+ async (t, a) => {
87
+ if (!e.find(
88
+ (A) => A.name === t.subAgentName
89
+ ))
90
+ throw new Error(`Sub-agent ${t.subAgentName} not found.`);
91
+ const o = this.getLastAiMessage(s), n = a.toolCall.id, h = await this.options.hooks.onAgentStart({
92
+ agentId: n,
93
+ agentName: t.subAgentName,
94
+ beloneMessageId: o?.id,
95
+ parentAgentId: s,
96
+ content: t.request
97
+ }), u = this.createAgent(h, n);
98
+ this.addMessage({
99
+ id: l(),
100
+ workId: this.id,
101
+ agentId: n,
102
+ role: "user",
103
+ content: t.request
104
+ });
105
+ const m = u.streamEvents(
106
+ {
107
+ messages: [{ role: "user", content: t.request }]
108
+ },
109
+ {
110
+ signal: this.abortController.signal,
111
+ context: {
112
+ ...a.context || {},
113
+ _agentCluster: this,
114
+ _agentId: n,
115
+ _parentAgentId: s
116
+ },
117
+ callbacks: [
118
+ new d({
119
+ agentCluster: this,
120
+ agentId: n
121
+ })
122
+ ]
123
+ }
124
+ );
125
+ return await Array.fromAsync(m), this.options.hooks.onAgentEnd?.(n), this.getLastAiMessage(n)?.content || "";
126
+ },
127
+ {
128
+ name: g.CALL_SUB_AGENT_NAME,
129
+ description: `Call a sub-agent to handle the user request.
130
+ The sub-agents are:
131
+ ${e.map((t) => `- ${t.name}: ${t.description}`).join(`
132
+ `)}
133
+ `,
134
+ schema: i.object({
135
+ subAgentName: i.literal(e.map((t) => t.name)).describe("The name of the sub-agent to call."),
136
+ request: i.string().describe("The request to send to the sub-agent.")
137
+ }),
138
+ extras: {
139
+ scope: "autoRun",
140
+ skipReport: !0
141
+ }
142
+ }
143
+ );
144
+ }
145
+ getLastAiMessage(e) {
146
+ for (let s = this.messages.length - 1; s >= 0; s--) {
147
+ const t = this.messages[s];
148
+ if (t.agentId === e && t.role === "assistant")
149
+ return t;
150
+ }
151
+ }
152
+ getMessages() {
153
+ return this.messages;
154
+ }
155
+ async revokeMessages(e) {
156
+ return this.options.messageCollector ? (await this.options.messageCollector.getMessagesByAgentId(
157
+ this.id,
158
+ e
159
+ )).map((t) => t.role === "user" ? new f(t.content) : t.role === "assistant" ? new T({
160
+ content: t.content,
161
+ tool_calls: t.toolCalls
162
+ }) : new _({
163
+ content: t.content || t.confirm?.result,
164
+ tool_call_id: t.id
165
+ })) : [];
166
+ }
167
+ }
168
+ export {
169
+ g as default
170
+ };
@@ -0,0 +1,90 @@
1
+ import { Readable as E } from "node:stream";
2
+ function P(i, n) {
3
+ const r = {}, s = {}, m = new E({
4
+ read() {
5
+ }
6
+ });
7
+ let d = Date.now();
8
+ function o(t) {
9
+ m.push(`${JSON.stringify(t)}
10
+
11
+ `), d = Date.now();
12
+ }
13
+ const p = setInterval(() => {
14
+ Date.now() - d > 20 * 1e3 && o({ type: "ping", data: void 0 });
15
+ }, 30 * 1e3);
16
+ function v() {
17
+ m.push(null), clearInterval(p);
18
+ }
19
+ function y(t, e) {
20
+ if (!s[t])
21
+ throw new Error(`not find confirm tool resolver: ${t}`);
22
+ s[t](e), delete s[t];
23
+ }
24
+ function w(t, e) {
25
+ if (!r[t])
26
+ throw new Error(`not find init agent resolver: ${t}`);
27
+ r[t](e), delete r[t];
28
+ }
29
+ return {
30
+ hooks: {
31
+ onChunk(t) {
32
+ a("onChunk", [t], n), o({ type: "chunk", data: { chunk: t } });
33
+ },
34
+ async onAgentStart(t) {
35
+ a("onAgentStart", [t], n), o({
36
+ type: "agentStart",
37
+ data: t
38
+ });
39
+ const { promise: e, resolve: c } = Promise.withResolvers();
40
+ r[t.agentId] = c;
41
+ const f = await e, l = await i(t.agentName), u = [
42
+ l?.systemPrompt || "",
43
+ f.systemPrompt || ""
44
+ ].filter(Boolean), A = (f.tools || []).map((g) => ({
45
+ ...g,
46
+ extras: {
47
+ ...g.extras,
48
+ remote: !0
49
+ }
50
+ }));
51
+ return {
52
+ ...l,
53
+ systemPrompt: u.length > 0 ? u.join(`
54
+ `) : void 0,
55
+ tools: [...l?.tools || [], ...A],
56
+ subAgents: [
57
+ ...l?.subAgents || [],
58
+ ...f.subAgents || []
59
+ ]
60
+ };
61
+ },
62
+ onAgentEnd(t) {
63
+ a("onAgentEnd", [t], n), o({ type: "agentEnd", data: { agentId: t } });
64
+ },
65
+ async onToolStart(t) {
66
+ if (a("onToolStart", [t], n), o({ type: "toolStart", data: { tool: t } }), t.needConfirm) {
67
+ const { resolve: e, promise: c } = Promise.withResolvers();
68
+ return s[t.toolCall.id] = e, c;
69
+ }
70
+ return {
71
+ type: "confirm"
72
+ };
73
+ },
74
+ onToolEnd(t, e) {
75
+ a("onToolEnd", [t, e], n), o({ type: "toolEnd", data: { toolPath: t, toolResult: e } });
76
+ }
77
+ },
78
+ stream: m,
79
+ send: o,
80
+ close: v,
81
+ resolveConfirmTool: y,
82
+ resolveAgentStart: w
83
+ };
84
+ }
85
+ function a(i, n, r) {
86
+ r?.forEach((s) => s[i]?.(...n));
87
+ }
88
+ export {
89
+ P as default
90
+ };
@@ -0,0 +1,13 @@
1
+ import { default as a } from "./cluster/instance.mjs";
2
+ import { default as l } from "./cluster/readableHook.mjs";
3
+ import { default as s } from "./cluster/fileMessageCollector.mjs";
4
+ import { default as m } from "./callback/LLMMessage.mjs";
5
+ import { default as u } from "./middleware/dynamicToolInterceptor.mjs";
6
+ import "@shuttle-ai/type";
7
+ export {
8
+ a as AgentCluster,
9
+ s as FileMessageCollector,
10
+ m as LLMMessage,
11
+ u as dynamicToolInterceptorMiddleware,
12
+ l as readableHook
13
+ };
@@ -0,0 +1,86 @@
1
+ import { createMiddleware as C } from "langchain";
2
+ import { DynamicTool as p, DynamicStructuredTool as u } from "@langchain/core/tools";
3
+ import { ToolMessage as s } from "@langchain/core/messages";
4
+ import w from "../cluster/instance.mjs";
5
+ const h = C({
6
+ name: "dynamicToolInterceptorMiddleware",
7
+ wrapToolCall: async (o, i) => {
8
+ const e = o.runtime.context, a = {
9
+ role: "tool",
10
+ name: o.toolCall.name,
11
+ id: o.toolCall.id || "",
12
+ aiMessageId: "",
13
+ agentId: e._agentId,
14
+ workId: e._agentCluster.id
15
+ };
16
+ let t;
17
+ if (o.tool instanceof p || o.tool instanceof u || o.tool && "extras" in o.tool) {
18
+ const l = o.tool.extras || {};
19
+ if (!l.skipReport) {
20
+ const m = !M(l, e._agentCluster), r = e._agentCluster.getLastAiMessage(
21
+ e._agentId
22
+ );
23
+ a.aiMessageId = r?.id || "";
24
+ const f = r?.toolCalls?.find(
25
+ (c) => c.id === o.toolCall.id
26
+ ), n = await e._agentCluster.options.hooks.onToolStart({
27
+ role: "assistant_tool",
28
+ toolCall: f,
29
+ needConfirm: l.remote || m,
30
+ id: r?.id || "",
31
+ agentId: e._agentId,
32
+ workId: e._agentCluster.id
33
+ });
34
+ if (a.confirm = n, n.type === "reject" || n.type === "confirm" && l.remote)
35
+ return e._agentCluster.addMessage(a), new s({
36
+ content: n.reason || "can not run tool",
37
+ tool_call_id: o.toolCall.id || ""
38
+ });
39
+ if (n.type === "confirmWithResult")
40
+ return e._agentCluster.addMessage(a), new s({
41
+ content: typeof n.result == "object" ? JSON.stringify(n.result) : n.result || "success",
42
+ tool_call_id: o.toolCall.id || ""
43
+ });
44
+ const d = {
45
+ agentId: e._agentId,
46
+ messageId: r?.id || "",
47
+ toolId: o.toolCall.id || ""
48
+ };
49
+ if (n.newArgs) {
50
+ o.toolCall.args = n.newArgs;
51
+ const g = o.state.messages[o.state.messages.length - 1].tool_calls?.find(
52
+ (_) => _.id === o.toolCall.id
53
+ );
54
+ g && (g.args = n.newArgs);
55
+ }
56
+ try {
57
+ t = await i(o), t instanceof s || "content" in t ? e._agentCluster.options.hooks.onToolEnd?.(
58
+ d,
59
+ t.content
60
+ ) : e._agentCluster.options.hooks.onToolEnd?.(d, "{}");
61
+ } catch (c) {
62
+ t = new s({
63
+ content: c.message || "can not run tool",
64
+ tool_call_id: o.toolCall.id || ""
65
+ });
66
+ }
67
+ }
68
+ }
69
+ if (!t)
70
+ try {
71
+ t = await i(o);
72
+ } catch (l) {
73
+ t = new s({
74
+ content: l.message || "can not run tool",
75
+ tool_call_id: o.toolCall.id || ""
76
+ });
77
+ }
78
+ return "lg_name" in t && t.lg_name === "Command" && (t = t?.update?.messages?.[0]), t instanceof s && t.name !== w.CALL_SUB_AGENT_NAME && (a.content = t.content, e._agentCluster.addMessage(a)), t;
79
+ }
80
+ });
81
+ function M(o, i) {
82
+ return o.scope === "autoRun" || i.options.autoRunScope === "always" || i.options.autoRunScope === "read" && o.scope !== "write";
83
+ }
84
+ export {
85
+ h as default
86
+ };
@@ -0,0 +1,15 @@
1
+ import { BaseCallbackHandler, NewTokenIndices } from '@langchain/core/callbacks/base';
2
+ import { LLMResult } from '@langchain/core/outputs';
3
+ import { AgentCluster } from '../cluster';
4
+ interface Options {
5
+ agentCluster: AgentCluster;
6
+ agentId: string;
7
+ }
8
+ export default class LLMMessage extends BaseCallbackHandler {
9
+ readonly options: Options;
10
+ readonly name = "LLMMessage";
11
+ constructor(options: Options);
12
+ handleLLMNewToken(token: string, idx: NewTokenIndices, runId: string): void;
13
+ handleLLMEnd(output: LLMResult, runId: string): void;
14
+ }
15
+ export {};
@@ -0,0 +1 @@
1
+ export { default as LLMMessage } from './LLMMessage';
@@ -0,0 +1,8 @@
1
+ import { ShuttleAi } from '@shuttle-ai/type';
2
+ export default class FileMessageCollector implements ShuttleAi.Cluster.MessageCollector {
3
+ private readonly dirPath;
4
+ constructor(dirPath: string);
5
+ saveMessage(message: ShuttleAi.Message.Define): Promise<void>;
6
+ getMessagesByAgentId(workId: string, agentId: string): Promise<ShuttleAi.Message.Define[]>;
7
+ getSubAgentTasks(workId: string, subAgentIds: string[]): Promise<ShuttleAi.Message.Define[]>;
8
+ }
@@ -0,0 +1,3 @@
1
+ export { default as AgentCluster } from './instance';
2
+ export { default as readableHook } from './readableHook';
3
+ export { default as FileMessageCollector } from './fileMessageCollector';
@@ -0,0 +1,34 @@
1
+ import { Runnable } from '@langchain/core/runnables';
2
+ import { CreateAgentParams } from 'langchain';
3
+ import { ClientTool } from '@langchain/core/tools';
4
+ import { ShuttleAi } from '@shuttle-ai/type';
5
+ import { z } from 'zod';
6
+ export default class AgentCluster extends Runnable {
7
+ readonly options: ShuttleAi.Cluster.Options;
8
+ static MAIN_AGENT_NAME: string;
9
+ static CALL_SUB_AGENT_NAME: string;
10
+ readonly lc_namespace: string[];
11
+ readonly id: string;
12
+ private messages;
13
+ readonly abortController: AbortController;
14
+ constructor(options: ShuttleAi.Cluster.Options);
15
+ addMessage(message: ShuttleAi.Message.Define): void;
16
+ invoke(input: string, options?: Omit<ShuttleAi.Cluster.InvokeOptions, keyof ShuttleAi.Cluster.SystemContext>): Promise<string>;
17
+ stop(resign?: string): void;
18
+ createAgent({ tools, subAgents, middleware, ...extra }: ShuttleAi.Cluster.ToolsWithSubAgents & Omit<CreateAgentParams, 'tools'>, agentId: string): import('langchain').ReactAgent<import('langchain').AgentTypeConfig<import('langchain').ResponseFormatUndefined, undefined, import('langchain').AnyAnnotationRoot, any, readonly ClientTool[]>>;
19
+ normalizationTools(tools: (ShuttleAi.Tool.Define | ClientTool)[]): (ClientTool | import('langchain').DynamicStructuredTool<import('@langchain/core/utils/types').ZodObjectV3, Record<string, any> | undefined, any, string, string>)[];
20
+ customToolToDynamicTool(_tool: ShuttleAi.Tool.Define): import('langchain').DynamicStructuredTool<import('@langchain/core/utils/types').ZodObjectV3, Record<string, any> | undefined, any, string, string>;
21
+ subAgentToDynamicTool(subAgents: ShuttleAi.SubAgent.Define[], agentId: string): import('langchain').DynamicStructuredTool<z.ZodObject<{
22
+ subAgentName: z.ZodLiteral<string>;
23
+ request: z.ZodString;
24
+ }, z.core.$strip>, {
25
+ subAgentName: string;
26
+ request: string;
27
+ }, {
28
+ subAgentName: string;
29
+ request: string;
30
+ }, string, string>;
31
+ getLastAiMessage(agentId: string): ShuttleAi.Message.AI | undefined;
32
+ getMessages(): ShuttleAi.Message.Define[];
33
+ private revokeMessages;
34
+ }
@@ -0,0 +1,16 @@
1
+ import { Readable } from 'stream';
2
+ import { ShuttleAi } from '@shuttle-ai/type';
3
+ import { CreateAgentParams } from 'langchain';
4
+ interface ReadableHookLink extends Omit<ShuttleAi.Cluster.Hooks, 'onAgentStart' | 'onToolStart'> {
5
+ onAgentStart?: (options: ShuttleAi.Ask.AgentStart['data']) => void;
6
+ onToolStart?: (tool: ShuttleAi.Message.AITool) => void;
7
+ }
8
+ export default function createReadableHook(getAgentParamsFromServer: (agentName: string) => Promise<ShuttleAi.Cluster.ToolsWithSubAgents & Omit<CreateAgentParams, 'tools'>>, hookLinks?: ReadableHookLink[]): {
9
+ hooks: ShuttleAi.Cluster.Hooks;
10
+ stream: Readable;
11
+ send: (data: ShuttleAi.Ask.Define) => void;
12
+ close: () => void;
13
+ resolveConfirmTool: (id: string, value: ShuttleAi.Tool.ConfirmResult) => void;
14
+ resolveAgentStart: (id: string, value: ShuttleAi.Report.AgentStart["data"]["params"]) => void;
15
+ };
16
+ export {};
@@ -0,0 +1,4 @@
1
+ export * from './cluster';
2
+ export * from './callback';
3
+ export * from './middleware';
4
+ export * from './type';
@@ -0,0 +1,2 @@
1
+ declare const dynamicToolInterceptorMiddleware: import('langchain').AgentMiddleware<undefined, undefined, unknown, readonly (import('@langchain/core/tools').ClientTool | import('@langchain/core/tools').ServerTool)[]>;
2
+ export default dynamicToolInterceptorMiddleware;
@@ -0,0 +1 @@
1
+ export { default as dynamicToolInterceptorMiddleware } from './dynamicToolInterceptor';
@@ -0,0 +1,66 @@
1
+ import { ClientTool } from '@langchain/core/tools';
2
+ import { CreateAgentParams } from 'langchain';
3
+ import { default as AgentCluster } from './cluster/instance';
4
+ declare module '@shuttle-ai/type' {
5
+ namespace ShuttleAi {
6
+ namespace Cluster {
7
+ type AutoRunScope = 'always' | 'read' | 'none';
8
+ interface Options {
9
+ /**
10
+ * The id of the agent cluster.
11
+ */
12
+ id?: string;
13
+ /**
14
+ * The scope of auto run.
15
+ * - `always`: Always run the agent.
16
+ * - `read`: Run the agent when the user asks a question.
17
+ * - `none`: Never run the agent.
18
+ * Default: `none`
19
+ */
20
+ autoRunScope?: AutoRunScope;
21
+ /**
22
+ * The hooks of the agent.
23
+ */
24
+ hooks: Hooks;
25
+ messageCollector?: MessageCollector;
26
+ /**
27
+ * The messages of the agent.
28
+ */
29
+ messages?: ShuttleAi.Message.Define[];
30
+ single?: AbortSignal;
31
+ }
32
+ interface ToolsWithSubAgents {
33
+ /**
34
+ * The tools of the agent.
35
+ */
36
+ tools?: (ShuttleAi.Tool.Define | ClientTool)[];
37
+ /**
38
+ * The sub-agents of the agent.
39
+ */
40
+ subAgents?: ShuttleAi.SubAgent.Define[];
41
+ }
42
+ interface Hooks {
43
+ onChunk?: (chunk: ShuttleAi.Message.AIChunk) => void;
44
+ onAgentStart: (options: ShuttleAi.Ask.AgentStart['data']) => Promise<ToolsWithSubAgents & Omit<CreateAgentParams, 'tools'>>;
45
+ onAgentEnd?: (agentId: string) => void;
46
+ onToolStart: (tool: ShuttleAi.Message.AITool) => Promise<ShuttleAi.Tool.ConfirmResult>;
47
+ onToolEnd?: (toolPath: ShuttleAi.Tool.Path, result: any) => void;
48
+ }
49
+ interface MessageCollector {
50
+ saveMessage: (message: ShuttleAi.Message.Define) => void;
51
+ getMessagesByAgentId: (workId: string, agentId: string) => Promise<ShuttleAi.Message.Define[]>;
52
+ }
53
+ interface SystemContext {
54
+ _agentCluster: AgentCluster;
55
+ _agentId: string;
56
+ _parentAgentId?: string;
57
+ }
58
+ interface Context extends SystemContext {
59
+ }
60
+ interface InvokeOptions {
61
+ context?: Context;
62
+ [x: string]: any;
63
+ }
64
+ }
65
+ }
66
+ }
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@shuttle-ai/agent",
3
+ "version": "0.0.1",
4
+ "author": "Mingbing-get <1508850533@qq.com>",
5
+ "license": "MIT",
6
+ "description": "Shuttle AI 智能体操作库",
7
+ "main": "./src/index.ts",
8
+ "module": "./src/index.ts",
9
+ "types": "./src/index.ts",
10
+ "type": "module",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./src/index.ts",
14
+ "import": "./src/index.ts",
15
+ "require": "./src/index.ts"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "vite build",
24
+ "clean": "rm -rf dist",
25
+ "type-check": "tsc --noEmit",
26
+ "test": "echo \"Error: no test specified\" && exit 1"
27
+ },
28
+ "dependencies": {
29
+ "@shuttle-ai/type": "workspace:*",
30
+ "langchain": "^1.2.17",
31
+ "@langchain/core": "^1.1.19",
32
+ "@langchain/openai": "^1.2.4",
33
+ "zod": "^4.3.5"
34
+ },
35
+ "devDependencies": {
36
+ "typescript": "^5.3.3",
37
+ "@types/node": "^20.11.0",
38
+ "mysql2": "^3.9.1",
39
+ "dotenv": "^17.2.3"
40
+ },
41
+ "access": "public",
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "main": "dist/cjs/index.cjs",
45
+ "module": "dist/es/index.mjs",
46
+ "types": "dist/types/index.d.ts",
47
+ "type": "module",
48
+ "exports": {
49
+ ".": {
50
+ "types": "./dist/types/index.d.ts",
51
+ "import": "./dist/es/index.mjs",
52
+ "require": "./dist/cjs/index.cjs"
53
+ }
54
+ }
55
+ }
56
+ }