@easynet/agent-runtime 1.0.3 → 1.0.4

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.
Files changed (205) hide show
  1. package/.github/workflows/ci.yml +9 -24
  2. package/.github/workflows/release.yml +14 -35
  3. package/agent-runtime/.github/workflows/ci.yml +69 -0
  4. package/agent-runtime/.github/workflows/release.yml +118 -0
  5. package/agent-runtime/.releaserc.cjs +26 -0
  6. package/agent-runtime/config/agent.deep.yaml +25 -0
  7. package/agent-runtime/config/agent.react.yaml +24 -0
  8. package/agent-runtime/example/basic-usage.ts +49 -0
  9. package/agent-runtime/package-lock.json +7740 -0
  10. package/agent-runtime/package.json +49 -0
  11. package/agent-runtime/pnpm-lock.yaml +3712 -0
  12. package/agent-runtime/scripts/resolve-deps.js +54 -0
  13. package/agent-runtime/src/agents/deep-agent.ts +165 -0
  14. package/agent-runtime/src/agents/react-agent.helpers.ts +227 -0
  15. package/agent-runtime/src/agents/react-agent.ts +584 -0
  16. package/{src → agent-runtime/src/agents}/sub-agent.ts +2 -2
  17. package/agent-runtime/src/cli/args.ts +15 -0
  18. package/agent-runtime/src/cli/event-listener.ts +162 -0
  19. package/agent-runtime/src/cli/interactive.ts +144 -0
  20. package/agent-runtime/src/cli/runtime.ts +31 -0
  21. package/agent-runtime/src/cli/spinner.ts +23 -0
  22. package/agent-runtime/src/cli/terminal-render.ts +322 -0
  23. package/agent-runtime/src/cli/types.ts +33 -0
  24. package/agent-runtime/src/cli.ts +134 -0
  25. package/agent-runtime/src/config/helpers.ts +179 -0
  26. package/agent-runtime/src/config/index.ts +245 -0
  27. package/agent-runtime/src/config/types.ts +62 -0
  28. package/agent-runtime/src/core/context.ts +266 -0
  29. package/agent-runtime/src/index.ts +55 -0
  30. package/agent-runtime/tsconfig.json +18 -0
  31. package/apps/imessagebot/README.md +38 -0
  32. package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/README.md +33 -0
  33. package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/package-lock.json +15257 -0
  34. package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/package.json +55 -0
  35. package/apps/imessagebot/config/agents/deep/agent.yaml +31 -0
  36. package/apps/imessagebot/config/agents/react/agent.yaml +58 -0
  37. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/README.md +33 -0
  38. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/package-lock.json +15457 -0
  39. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/package.json +55 -0
  40. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/README.md +33 -0
  41. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/package-lock.json +15257 -0
  42. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/package.json +62 -0
  43. package/apps/imessagebot/config/agents/shared/memory.yaml +31 -0
  44. package/apps/imessagebot/config/agents/shared/model.yaml +23 -0
  45. package/apps/imessagebot/config/agents/shared/tool.yaml +13 -0
  46. package/apps/imessagebot/config/app.yaml +14 -0
  47. package/apps/imessagebot/package-lock.json +53695 -0
  48. package/apps/imessagebot/package.json +41 -0
  49. package/apps/imessagebot/pnpm-lock.yaml +1589 -0
  50. package/apps/imessagebot/scripts/resolve-deps.js +41 -0
  51. package/apps/imessagebot/scripts/test-llm.mjs +27 -0
  52. package/apps/imessagebot/scripts/validate-tools-config.mjs +174 -0
  53. package/apps/imessagebot/src/config.ts +76 -0
  54. package/apps/imessagebot/src/context.ts +35 -0
  55. package/apps/imessagebot/src/index.ts +17 -0
  56. package/apps/imessagebot/tsconfig.json +18 -0
  57. package/apps/itermbot/.github/workflows/ci.yml +61 -0
  58. package/apps/itermbot/.github/workflows/release.yml +80 -0
  59. package/apps/itermbot/.releaserc.cjs +26 -0
  60. package/apps/itermbot/README.md +82 -0
  61. package/apps/itermbot/config/app.yaml +29 -0
  62. package/apps/itermbot/config/tsconfig.json +18 -0
  63. package/apps/itermbot/macos_disk_usage_agent_plan.md +244 -0
  64. package/apps/itermbot/package-lock.json +53697 -0
  65. package/apps/itermbot/package.json +57 -0
  66. package/apps/itermbot/pnpm-lock.yaml +3966 -0
  67. package/apps/itermbot/scripts/patch-buildin-cache.sh +25 -0
  68. package/apps/itermbot/scripts/resolve-deps.js +41 -0
  69. package/apps/itermbot/scripts/test-llm.mjs +32 -0
  70. package/apps/itermbot/skills/command-explain-and-guard/SKILL.md +39 -0
  71. package/apps/itermbot/skills/command-explain-and-guard/handler.js +86 -0
  72. package/apps/itermbot/skills/disk-usage-investigate/SKILL.md +44 -0
  73. package/apps/itermbot/skills/disk-usage-investigate/handler.js +12 -0
  74. package/apps/itermbot/skills/gpu-ssh-monitor/SKILL.md +64 -0
  75. package/apps/itermbot/skills/repo-triage/SKILL.md +40 -0
  76. package/apps/itermbot/skills/repo-triage/handler.js +56 -0
  77. package/apps/itermbot/skills/test-failure-diagnose/SKILL.md +43 -0
  78. package/apps/itermbot/skills/test-failure-diagnose/handler.js +107 -0
  79. package/apps/itermbot/src/config.ts +95 -0
  80. package/apps/itermbot/src/context.ts +35 -0
  81. package/apps/itermbot/src/index.ts +223 -0
  82. package/apps/itermbot/src/iterm/session-hint.ts +40 -0
  83. package/apps/itermbot/src/iterm/target-routing.ts +419 -0
  84. package/apps/itermbot/src/startup/colors.ts +317 -0
  85. package/apps/itermbot/src/startup/diagnostics.ts +97 -0
  86. package/apps/itermbot/src/startup/ui.ts +141 -0
  87. package/config/agent.deep.yaml +25 -0
  88. package/config/agent.react.yaml +24 -0
  89. package/dist/agents/deep-agent.d.ts +37 -0
  90. package/dist/agents/deep-agent.d.ts.map +1 -0
  91. package/dist/agents/deep-agent.js +115 -0
  92. package/dist/agents/deep-agent.js.map +1 -0
  93. package/dist/agents/react-agent.d.ts +40 -0
  94. package/dist/agents/react-agent.d.ts.map +1 -0
  95. package/dist/agents/react-agent.helpers.d.ts +40 -0
  96. package/dist/agents/react-agent.helpers.d.ts.map +1 -0
  97. package/dist/agents/react-agent.helpers.js +196 -0
  98. package/dist/agents/react-agent.helpers.js.map +1 -0
  99. package/dist/agents/react-agent.js +400 -0
  100. package/dist/agents/react-agent.js.map +1 -0
  101. package/dist/agents/sub-agent.d.ts +34 -0
  102. package/dist/agents/sub-agent.d.ts.map +1 -0
  103. package/dist/agents/sub-agent.js +53 -0
  104. package/dist/agents/sub-agent.js.map +1 -0
  105. package/dist/cli/args.d.ts +8 -0
  106. package/dist/cli/args.d.ts.map +1 -0
  107. package/dist/cli/args.js +9 -0
  108. package/dist/cli/args.js.map +1 -0
  109. package/dist/cli/event-listener.d.ts +3 -0
  110. package/dist/cli/event-listener.d.ts.map +1 -0
  111. package/dist/cli/event-listener.js +131 -0
  112. package/dist/cli/event-listener.js.map +1 -0
  113. package/dist/cli/interactive.d.ts +4 -0
  114. package/dist/cli/interactive.d.ts.map +1 -0
  115. package/dist/cli/interactive.js +118 -0
  116. package/dist/cli/interactive.js.map +1 -0
  117. package/dist/cli/runtime.d.ts +8 -0
  118. package/dist/cli/runtime.d.ts.map +1 -0
  119. package/dist/cli/runtime.js +27 -0
  120. package/dist/cli/runtime.js.map +1 -0
  121. package/dist/cli/spinner.d.ts +2 -0
  122. package/dist/cli/spinner.d.ts.map +1 -0
  123. package/dist/cli/spinner.js +22 -0
  124. package/dist/cli/spinner.js.map +1 -0
  125. package/dist/cli/terminal-render.d.ts +7 -0
  126. package/dist/cli/terminal-render.d.ts.map +1 -0
  127. package/dist/cli/terminal-render.js +282 -0
  128. package/dist/cli/terminal-render.js.map +1 -0
  129. package/dist/cli/types.d.ts +29 -0
  130. package/dist/cli/types.d.ts.map +1 -0
  131. package/dist/cli/types.js +3 -0
  132. package/dist/cli/types.js.map +1 -0
  133. package/dist/cli.d.ts +4 -41
  134. package/dist/cli.d.ts.map +1 -1
  135. package/dist/cli.js +84 -588
  136. package/dist/cli.js.map +1 -1
  137. package/dist/config/helpers.d.ts +6 -0
  138. package/dist/config/helpers.d.ts.map +1 -0
  139. package/dist/config/helpers.js +164 -0
  140. package/dist/config/helpers.js.map +1 -0
  141. package/dist/config/index.d.ts +15 -0
  142. package/dist/config/index.d.ts.map +1 -0
  143. package/dist/config/index.js +160 -0
  144. package/dist/config/index.js.map +1 -0
  145. package/dist/config/types.d.ts +57 -0
  146. package/dist/config/types.d.ts.map +1 -0
  147. package/dist/config/types.js +2 -0
  148. package/dist/config/types.js.map +1 -0
  149. package/dist/context.d.ts +8 -69
  150. package/dist/context.d.ts.map +1 -1
  151. package/dist/context.js +44 -24
  152. package/dist/context.js.map +1 -1
  153. package/dist/core/context.d.ts +66 -0
  154. package/dist/core/context.d.ts.map +1 -0
  155. package/dist/core/context.js +149 -0
  156. package/dist/core/context.js.map +1 -0
  157. package/dist/deep-agent.d.ts +5 -2
  158. package/dist/deep-agent.d.ts.map +1 -1
  159. package/dist/deep-agent.js +44 -11
  160. package/dist/deep-agent.js.map +1 -1
  161. package/dist/index.d.ts +6 -6
  162. package/dist/index.d.ts.map +1 -1
  163. package/dist/index.js +6 -6
  164. package/dist/index.js.map +1 -1
  165. package/dist/middleware/malformed-tool-call-middleware.d.ts +8 -0
  166. package/dist/middleware/malformed-tool-call-middleware.d.ts.map +1 -0
  167. package/dist/middleware/malformed-tool-call-middleware.js +191 -0
  168. package/dist/middleware/malformed-tool-call-middleware.js.map +1 -0
  169. package/dist/react-agent.d.ts +2 -2
  170. package/dist/react-agent.d.ts.map +1 -1
  171. package/dist/react-agent.js +28 -9
  172. package/dist/react-agent.js.map +1 -1
  173. package/package.json +1 -1
  174. package/scripts/resolve-deps.js +54 -0
  175. package/src/agents/deep-agent.ts +165 -0
  176. package/src/agents/react-agent.helpers.ts +227 -0
  177. package/src/agents/react-agent.ts +584 -0
  178. package/src/agents/sub-agent.ts +82 -0
  179. package/src/cli/args.ts +15 -0
  180. package/src/cli/event-listener.ts +162 -0
  181. package/src/cli/interactive.ts +144 -0
  182. package/src/cli/runtime.ts +31 -0
  183. package/src/cli/spinner.ts +23 -0
  184. package/src/cli/terminal-render.ts +322 -0
  185. package/src/cli/types.ts +33 -0
  186. package/src/cli.ts +91 -702
  187. package/src/config/helpers.ts +179 -0
  188. package/src/config/index.ts +245 -0
  189. package/src/config/types.ts +62 -0
  190. package/src/core/context.ts +266 -0
  191. package/src/index.ts +13 -11
  192. package/src/middleware/malformed-tool-call-middleware.ts +239 -0
  193. package/src/types/markdown-it-terminal.d.ts +4 -0
  194. package/src/types/marked-terminal.d.ts +16 -0
  195. package/dist/config.d.ts +0 -86
  196. package/dist/config.d.ts.map +0 -1
  197. package/dist/config.js +0 -84
  198. package/dist/config.js.map +0 -1
  199. package/src/config.ts +0 -177
  200. package/src/context.ts +0 -247
  201. package/src/deep-agent.ts +0 -104
  202. package/src/react-agent.ts +0 -576
  203. /package/{src → agent-runtime/src/middleware}/malformed-tool-call-middleware.ts +0 -0
  204. /package/{src → agent-runtime/src/types}/markdown-it-terminal.d.ts +0 -0
  205. /package/{src → agent-runtime/src/types}/marked-terminal.d.ts +0 -0
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * resolve-deps.js
4
+ *
5
+ * Run automatically via `preinstall` npm hook.
6
+ * In CI (CI=true), rewrites file: references in package.json to npm versions
7
+ * so that `npm install` can resolve them without local sibling directories.
8
+ *
9
+ * Local development: no-op (CI env var is not set).
10
+ */
11
+
12
+ import { readFileSync, writeFileSync } from 'fs';
13
+ import { resolve, dirname } from 'path';
14
+ import { fileURLToPath } from 'url';
15
+
16
+ const __dirname = dirname(fileURLToPath(import.meta.url));
17
+ const pkgPath = resolve(__dirname, '..', 'package.json');
18
+
19
+ if (!process.env.CI) {
20
+ process.exit(0);
21
+ }
22
+
23
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
24
+ let changed = false;
25
+
26
+ // agent-skill is a private package — use git ref in CI
27
+ const AGENT_SKILL_TOKEN = process.env.AGENT_SKILL_READ_TOKEN || process.env.GITHUB_TOKEN || '';
28
+
29
+ for (const section of ['dependencies', 'devDependencies']) {
30
+ const deps = pkg[section];
31
+ if (!deps) continue;
32
+ for (const [name, version] of Object.entries(deps)) {
33
+ if (name.startsWith('@easynet/') && typeof version === 'string' && version.startsWith('file:')) {
34
+ let resolved;
35
+ if (name === '@easynet/agent-skill') {
36
+ if (!AGENT_SKILL_TOKEN) {
37
+ console.error('[resolve-deps] Missing AGENT_SKILL_READ_TOKEN (or GITHUB_TOKEN) for @easynet/agent-skill.');
38
+ process.exit(1);
39
+ }
40
+ resolved = `git+https://x-access-token:${AGENT_SKILL_TOKEN}@github.com/easynet-world/agent-skill.git#master`;
41
+ } else {
42
+ resolved = 'latest';
43
+ }
44
+ deps[name] = resolved;
45
+ changed = true;
46
+ console.log(`[resolve-deps] ${name}: file: → ${name === '@easynet/agent-skill' ? 'git+https://...' : resolved}`);
47
+ }
48
+ }
49
+ }
50
+
51
+ if (changed) {
52
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
53
+ console.log('[resolve-deps] package.json updated for CI.');
54
+ }
@@ -0,0 +1,165 @@
1
+ import {
2
+ createDeepAgent as createDeepAgentRuntime,
3
+ StateBackend,
4
+ } from "deepagents";
5
+ import { asObject, extractLastMessageText } from "@easynet/agent-common";
6
+ import type { BaseAgentConfig, BotContext } from "../core/context.js";
7
+
8
+ export interface DeepAgentRuntime {
9
+ agent: unknown;
10
+ run: (userMessage: string) => Promise<{ text: string; messages?: unknown }>;
11
+ }
12
+
13
+ export interface DeepAgentOptions {
14
+ agentName?: string;
15
+ systemPrompt?: string;
16
+ recursionLimit?: number;
17
+ autoWriteMemory?: boolean;
18
+ printSteps?: boolean;
19
+ fallbackText?: string;
20
+ commandWindowLabel?: string;
21
+ }
22
+
23
+ type Publish = (name: string, from: string, to: string, payload?: unknown) => void;
24
+
25
+ interface DeepRuntimeConfig {
26
+ recursionLimit?: number;
27
+ printSteps: boolean;
28
+ fallbackText: string;
29
+ commandWindowLabel?: string;
30
+ namespace?: string;
31
+ autoWriteMemory: boolean;
32
+ autoWriteScope: "thread" | "cross_thread" | "knowledge" | "auto";
33
+ autoWriteMaxChars: number;
34
+ autoWriteUser: boolean;
35
+ autoWriteAssistant: boolean;
36
+ systemPrompt?: string;
37
+ }
38
+
39
+ function readAppString(appConfig: unknown, key: string): string | undefined {
40
+ const app = asObject(appConfig);
41
+ const value = app?.[key];
42
+ return typeof value === "string" ? value : undefined;
43
+ }
44
+
45
+ function readAppBoolean(appConfig: unknown, key: string): boolean | undefined {
46
+ const app = asObject(appConfig);
47
+ const value = app?.[key];
48
+ return typeof value === "boolean" ? value : undefined;
49
+ }
50
+
51
+ function pickFirstAgentName(config: BaseAgentConfig | undefined): string | undefined {
52
+ return Object.keys(config?.app?.agent ?? {})[0];
53
+ }
54
+
55
+ function pickAgentConfig(config: BaseAgentConfig | undefined, agentName?: string) {
56
+ if (!agentName) return undefined;
57
+ return config?.app?.agent?.[agentName];
58
+ }
59
+
60
+ function resolveConfig<TConfig extends BaseAgentConfig>(ctx: BotContext<TConfig>, options: DeepAgentOptions): DeepRuntimeConfig {
61
+ const agentName = options.agentName ?? pickFirstAgentName(ctx.config);
62
+ const configDeep = pickAgentConfig(ctx.config, agentName);
63
+ const appConfig = ctx.config.app;
64
+ return {
65
+ recursionLimit: options.recursionLimit ?? configDeep?.recursionLimit,
66
+ printSteps: options.printSteps ?? configDeep?.printSteps ?? readAppBoolean(appConfig, "printSteps") ?? false,
67
+ fallbackText: options.fallbackText ?? configDeep?.fallbackText ?? readAppString(appConfig, "fallbackText") ?? "",
68
+ commandWindowLabel: options.commandWindowLabel ?? configDeep?.commandWindowLabel ?? readAppString(appConfig, "commandWindowLabel"),
69
+ namespace: configDeep?.memory?.namespace,
70
+ autoWriteMemory: options.autoWriteMemory ?? (configDeep?.memory?.auto_write ?? true),
71
+ autoWriteScope: configDeep?.memory?.auto_write_scope ?? "thread",
72
+ autoWriteMaxChars: configDeep?.memory?.auto_write_max_chars ?? 2400,
73
+ autoWriteUser: configDeep?.memory?.auto_write_user ?? true,
74
+ autoWriteAssistant: configDeep?.memory?.auto_write_assistant ?? true,
75
+ systemPrompt: options.systemPrompt ?? configDeep?.systemPrompt,
76
+ };
77
+ }
78
+
79
+ function resolveFinalText(text: string, fallbackText: string, commandWindowLabel?: string): string {
80
+ if (text.trim().length > 0) return text;
81
+ if (fallbackText.trim().length > 0) return fallbackText;
82
+ return commandWindowLabel ? `Command sent to ${commandWindowLabel}.` : text;
83
+ }
84
+
85
+ async function autoWriteIfNeeded<TConfig extends BaseAgentConfig>(
86
+ ctx: BotContext<TConfig>,
87
+ config: DeepRuntimeConfig,
88
+ userMessage: string,
89
+ finalText: string,
90
+ publish: Publish,
91
+ ): Promise<void> {
92
+ if (!config.namespace || !config.autoWriteMemory) return;
93
+
94
+ const scope = config.autoWriteScope === "auto" ? "thread" : config.autoWriteScope;
95
+ const writeItems: string[] = [];
96
+ if (config.autoWriteUser && userMessage.trim().length > 0) writeItems.push(`[user]\n${userMessage.trim()}`);
97
+ if (config.autoWriteAssistant && finalText.trim().length > 0) writeItems.push(`[assistant]\n${finalText.trim()}`);
98
+
99
+ const content = writeItems.join("\n\n").slice(0, Math.max(0, config.autoWriteMaxChars));
100
+ if (!content.length) return;
101
+
102
+ try {
103
+ publish("agent.deep.memory.write.start", "deep-agent", "memory", { type: scope, auto: true });
104
+ await ctx.memory.memorize(config.namespace, scope, content);
105
+ publish("agent.deep.memory.write.done", "memory", "deep-agent", { type: scope, auto: true });
106
+ } catch (err) {
107
+ publish("agent.deep.memory.write.error", "memory", "deep-agent", {
108
+ auto: true,
109
+ error: err instanceof Error ? err.message : String(err),
110
+ });
111
+ }
112
+ }
113
+
114
+ function updateConversation(history: Array<{ role: "user" | "assistant"; content: string }>, userMessage: string, finalText: string): void {
115
+ history.push({ role: "user", content: userMessage });
116
+ if (finalText.trim().length > 0) history.push({ role: "assistant", content: finalText });
117
+ if (history.length > 24) history.splice(0, history.length - 24);
118
+ }
119
+
120
+ function createAgent<TConfig extends BaseAgentConfig>(ctx: BotContext<TConfig>, systemPrompt?: string) {
121
+ type BackendConfig = { state: unknown };
122
+ return createDeepAgentRuntime({
123
+ model: ctx.llm,
124
+ tools: ctx.tools as unknown as Array<Record<string, unknown>>,
125
+ backend: (config: BackendConfig) => new StateBackend(config),
126
+ systemPrompt,
127
+ } as never) as unknown as {
128
+ invoke: (input: { messages: Array<{ role: "user" | "assistant"; content: string }> }) => Promise<unknown>;
129
+ withConfig?: (config: { recursionLimit: number }) => { invoke: (input: unknown) => Promise<unknown> };
130
+ };
131
+ }
132
+
133
+ export async function createDeepAgent<TConfig extends BaseAgentConfig>(ctx: BotContext<TConfig>, options: DeepAgentOptions = {}) {
134
+ const publish: Publish = (name, from, to, payload) => {
135
+ ctx.events.publish({ name, from, to, payload });
136
+ };
137
+
138
+ publish("agent.deep.init.start", "app", "deep-agent");
139
+ const config = resolveConfig(ctx, options);
140
+ const agent = createAgent(ctx, config.systemPrompt);
141
+ const conversationHistory: Array<{ role: "user" | "assistant"; content: string }> = [];
142
+ publish("agent.deep.init.done", "deep-agent", "app");
143
+
144
+ return {
145
+ agent,
146
+ async run(userMessage: string): Promise<{ text: string; messages?: unknown }> {
147
+ publish("agent.deep.run.start", "user", "deep-agent", { userMessage });
148
+ if (config.printSteps) console.error(`[deep] user: ${userMessage}`);
149
+
150
+ const runnable = typeof config.recursionLimit === "number"
151
+ ? (agent.withConfig?.({ recursionLimit: config.recursionLimit }) ?? agent)
152
+ : agent;
153
+ const result = await runnable.invoke({ messages: [...conversationHistory, { role: "user", content: userMessage }] });
154
+ const messages = (result as { messages?: unknown } | null | undefined)?.messages ?? result;
155
+ const finalText = resolveFinalText(extractLastMessageText(result), config.fallbackText, config.commandWindowLabel);
156
+
157
+ updateConversation(conversationHistory, userMessage, finalText);
158
+ await autoWriteIfNeeded(ctx, config, userMessage, finalText, publish);
159
+ if (config.printSteps) console.error(`[deep] assistant: ${finalText}`);
160
+
161
+ publish("agent.deep.run.done", "deep-agent", "user", { text: finalText });
162
+ return { text: finalText, messages };
163
+ },
164
+ };
165
+ }
@@ -0,0 +1,227 @@
1
+ import type { BaseMessageLike } from "@langchain/core/messages";
2
+ import type { LanguageModelLike } from "@langchain/core/language_models/base";
3
+ import { asObject } from "@easynet/agent-common";
4
+ import type { BaseAgentConfig } from "../core/context.js";
5
+
6
+ export interface MessageLike {
7
+ type?: string;
8
+ role?: string;
9
+ content?: string | unknown;
10
+ tool_calls?: Array<{ name?: string; args?: unknown; id?: string }>;
11
+ name?: string;
12
+ tool_call_id?: string;
13
+ }
14
+
15
+ export interface ToolRunStats {
16
+ invoked: number;
17
+ succeeded: number;
18
+ failed: number;
19
+ lastError?: string;
20
+ }
21
+
22
+ function toMessageText(content: unknown): string {
23
+ if (typeof content === "string") return content;
24
+ if (!Array.isArray(content)) return "";
25
+ return content
26
+ .map((block) => {
27
+ if (typeof block === "string") return block;
28
+ if (typeof block === "object" && block !== null && "text" in block) {
29
+ const text = (block as { text?: unknown }).text;
30
+ return typeof text === "string" ? text : "";
31
+ }
32
+ return "";
33
+ })
34
+ .join("");
35
+ }
36
+
37
+ export function inferMemoryTypeByStructure(content: string): "thread" | "cross_thread" {
38
+ const text = content.trim();
39
+ if (!text) return "thread";
40
+
41
+ const lines = text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
42
+ const duplicateRatio = lines.length > 0 ? 1 - new Set(lines).size / lines.length : 0;
43
+ const symbolCount = (text.match(/[^\p{L}\p{N}\s]/gu) ?? []).length;
44
+ const symbolRatio = symbolCount / Math.max(1, text.length);
45
+
46
+ if (text.length > 1200) return "thread";
47
+ if (lines.length > 18) return "thread";
48
+ if (duplicateRatio > 0.35) return "thread";
49
+ if (symbolRatio > 0.22) return "thread";
50
+ return "cross_thread";
51
+ }
52
+
53
+ export async function inferMemoryTypeWithModel(
54
+ llm: LanguageModelLike,
55
+ content: string,
56
+ ): Promise<"thread" | "cross_thread" | null> {
57
+ const classifierPrompt = [
58
+ "Classify memory scope for an AI agent.",
59
+ "Return strict JSON only: {\"type\":\"thread|cross_thread\",\"confidence\":0..1}.",
60
+ "Decision policy:",
61
+ "- thread: temporary session context, command output, logs, transient task state, step-by-step investigation details.",
62
+ "- cross_thread: stable user preferences, durable profile facts, long-lived constraints reusable in future sessions.",
63
+ "Use semantic judgment, not keyword matching.",
64
+ ].join("\n");
65
+
66
+ try {
67
+ const response = await llm.invoke([
68
+ { role: "system", content: classifierPrompt },
69
+ { role: "user", content },
70
+ ]);
71
+ const text = toMessageText((response as { content?: unknown }).content).trim();
72
+ const jsonBlob = text.match(/\{[\s\S]*\}/)?.[0] ?? text;
73
+ const parsed = JSON.parse(jsonBlob) as { type?: unknown; confidence?: unknown };
74
+ const type = parsed.type === "thread" || parsed.type === "cross_thread" ? parsed.type : null;
75
+ const confidence = typeof parsed.confidence === "number" ? parsed.confidence : 0;
76
+ return type && confidence >= 0.6 ? type : null;
77
+ } catch {
78
+ return null;
79
+ }
80
+ }
81
+
82
+ export function deriveFallbackTextFromMessages(
83
+ messages: BaseMessageLike[],
84
+ opts: { fallbackText: string; commandWindowLabel?: string },
85
+ ): string {
86
+ for (let i = messages.length - 1; i >= 0; i -= 1) {
87
+ const msg = messages[i] as MessageLike;
88
+ const type = msg?.type ?? msg?.role ?? "";
89
+ if (type !== "tool" && !msg?.tool_call_id) continue;
90
+
91
+ const raw = typeof msg.content === "string"
92
+ ? msg.content
93
+ : msg.content != null
94
+ ? String(msg.content)
95
+ : "";
96
+ if (!raw.trim()) continue;
97
+
98
+ try {
99
+ const parsed = JSON.parse(raw) as {
100
+ result?: { command?: string; windowId?: number; tabIndex?: number; to?: string; serviceType?: string };
101
+ evidence?: Array<{ summary?: string }>;
102
+ };
103
+
104
+ const summary = parsed.evidence?.find((item) => item?.summary)?.summary;
105
+ if (typeof summary === "string" && summary.trim()) return summary.trim();
106
+
107
+ const to = parsed.result?.to?.trim();
108
+ const serviceType = parsed.result?.serviceType?.trim();
109
+ if (to) return serviceType ? `Sent ${serviceType} message to ${to}.` : `Sent message to ${to}.`;
110
+
111
+ const command = parsed.result?.command?.trim();
112
+ if (!command) continue;
113
+ const windowId = parsed.result?.windowId;
114
+ const tabIndex = parsed.result?.tabIndex;
115
+ if (typeof windowId === "number" && typeof tabIndex === "number") {
116
+ return `Executed command in ${opts.commandWindowLabel ?? "window"} ${windowId}, tab ${tabIndex}: ${command}`;
117
+ }
118
+ return `Executed command: ${command}`;
119
+ } catch {
120
+ return raw.length > 200 ? `${raw.slice(0, 200)}...` : raw;
121
+ }
122
+ }
123
+ return opts.fallbackText;
124
+ }
125
+
126
+ export function hasToolMessages(messages: unknown): boolean {
127
+ const list = Array.isArray(messages) ? messages : [];
128
+ for (const item of list) {
129
+ const msg = item as MessageLike;
130
+ const type = msg?.type ?? msg?.role ?? "";
131
+ if (type === "tool" || Boolean(msg?.tool_call_id) || (msg?.tool_calls?.length ?? 0) > 0) return true;
132
+ }
133
+ return false;
134
+ }
135
+
136
+ export function extractSchemaKeys(schema: unknown): string[] {
137
+ if (!schema || typeof schema !== "object") return [];
138
+ const rec = schema as Record<string, unknown>;
139
+ const properties = rec.properties;
140
+ if (properties && typeof properties === "object" && !Array.isArray(properties)) {
141
+ return Object.keys(properties as Record<string, unknown>);
142
+ }
143
+ const shape = rec.shape;
144
+ if (shape && typeof shape === "object" && !Array.isArray(shape)) {
145
+ return Object.keys(shape as Record<string, unknown>);
146
+ }
147
+ return [];
148
+ }
149
+
150
+ export function buildPtcHint(input: { taskIntent: string; stats: ToolRunStats; toolSignatures: string[] }): string {
151
+ return [
152
+ "[PTC_FEEDBACK]",
153
+ `TaskIntent: ${input.taskIntent}`,
154
+ `ToolStats: invoked=${input.stats.invoked}, succeeded=${input.stats.succeeded}, failed=${input.stats.failed}`,
155
+ input.stats.lastError ? `LastToolError: ${input.stats.lastError}` : "",
156
+ "Action: Retry using available tools. Do not claim tools are unavailable.",
157
+ "Action: If previous tool args were invalid, correct arguments strictly by schema.",
158
+ "Action: If a tool is needed, emit a valid tool call instead of plain explanation.",
159
+ `AvailableTools: ${input.toolSignatures.join("; ")}`,
160
+ ].filter((line) => line.length > 0).join("\n");
161
+ }
162
+
163
+ export function readAppString(appConfig: unknown, key: string): string | undefined {
164
+ const app = asObject(appConfig);
165
+ const value = app?.[key];
166
+ return typeof value === "string" ? value : undefined;
167
+ }
168
+
169
+ export function readAppBoolean(appConfig: unknown, key: string): boolean | undefined {
170
+ const app = asObject(appConfig);
171
+ const value = app?.[key];
172
+ return typeof value === "boolean" ? value : undefined;
173
+ }
174
+
175
+ export function pickFirstAgentName(config: BaseAgentConfig | undefined): string | undefined {
176
+ return Object.keys(config?.app?.agent ?? {})[0];
177
+ }
178
+
179
+ export function pickAgentConfig(config: BaseAgentConfig | undefined, agentName?: string) {
180
+ if (!agentName) return undefined;
181
+ return config?.app?.agent?.[agentName];
182
+ }
183
+
184
+ export function printReactSteps(messages: unknown): void {
185
+ const list = Array.isArray(messages) ? messages : [];
186
+ let stepIndex = 0;
187
+
188
+ for (let i = 0; i < list.length; i += 1) {
189
+ const msg = list[i] as MessageLike;
190
+ const type = msg?.type ?? msg?.role ?? "";
191
+ const content = msg?.content;
192
+ const contentStr = typeof content === "string"
193
+ ? content
194
+ : typeof content === "object" && content !== null && Array.isArray(content)
195
+ ? (content as { text?: string }[]).map((block) => block.text ?? JSON.stringify(block)).join("")
196
+ : content != null
197
+ ? String(content)
198
+ : "";
199
+
200
+ if (type === "human" || (type === "user" && !msg.tool_calls)) {
201
+ if (contentStr.trim()) console.error(`\n--- Step (user) ---\n${contentStr.trim()}`);
202
+ continue;
203
+ }
204
+
205
+ if (type === "ai" || type === "assistant") {
206
+ if (!msg.tool_calls?.length) continue;
207
+ stepIndex += 1;
208
+ if (contentStr.trim()) console.error(`\n--- ReAct Step ${stepIndex}: Thought ---\n${contentStr.trim()}`);
209
+ for (const toolCall of msg.tool_calls) {
210
+ const name = toolCall?.name ?? "?";
211
+ const args = typeof toolCall?.args === "object" && toolCall?.args !== null
212
+ ? JSON.stringify(toolCall.args)
213
+ : String(toolCall?.args ?? "");
214
+ console.error(`--- ReAct Step ${stepIndex}: Action ---\n${name}(${args})`);
215
+ }
216
+ continue;
217
+ }
218
+
219
+ if (type === "tool" || msg?.tool_call_id) {
220
+ const toolName = msg?.name ?? "tool";
221
+ const observation = contentStr.length > 200 ? `${contentStr.slice(0, 200)}...` : contentStr;
222
+ console.error(`--- ReAct Step ${stepIndex}: Observation (${toolName}) ---\n${observation || "(empty)"}`);
223
+ }
224
+ }
225
+
226
+ console.error("");
227
+ }