@botbotgo/agent-harness 0.0.324 → 0.0.325

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 (38) hide show
  1. package/README.md +2 -1
  2. package/README.zh.md +2 -1
  3. package/dist/cli/main.js +84 -2
  4. package/dist/cli/managed-service.d.ts +2 -1
  5. package/dist/cli/managed-service.js +78 -2
  6. package/dist/cli/options-serve.d.ts +13 -0
  7. package/dist/cli/options-serve.js +116 -0
  8. package/dist/cli/options.d.ts +1 -1
  9. package/dist/cli/options.js +7 -1
  10. package/dist/cli/server-commands.d.ts +1 -1
  11. package/dist/cli/server-commands.js +28 -8
  12. package/dist/config/knowledge/knowledge-runtime.yaml +3 -2
  13. package/dist/config/prompts/orchestra-system.md +4 -0
  14. package/dist/config/runtime/runtime-memory.yaml +3 -2
  15. package/dist/knowledge/module.js +2 -1
  16. package/dist/package-version.d.ts +2 -2
  17. package/dist/package-version.js +2 -2
  18. package/dist/resource/isolation.js +28 -0
  19. package/dist/resources/prompts/runtime/autonomous-investigation-recovery.md +1 -1
  20. package/dist/resources/prompts/runtime/durable-memory-context.md +2 -0
  21. package/dist/resources/prompts/runtime/execution-with-tool-evidence-retry.md +1 -1
  22. package/dist/resources/prompts/runtime/write-todos-required-plan.md +1 -0
  23. package/dist/runtime/adapter/flow/stream-runtime.js +9 -141
  24. package/dist/runtime/adapter/model/invocation-request.js +1 -1
  25. package/dist/runtime/adapter/tool/builtin-middleware-tools.js +22 -8
  26. package/dist/runtime/harness/run/stream-run.js +17 -0
  27. package/dist/runtime/harness/system/runtime-memory-manager.js +4 -0
  28. package/dist/runtime/harness/system/runtime-memory-policy.d.ts +14 -0
  29. package/dist/runtime/harness/system/runtime-memory-policy.js +19 -0
  30. package/dist/runtime/parsing/output-parsing.d.ts +2 -1
  31. package/dist/runtime/parsing/output-parsing.js +2 -1
  32. package/dist/runtime/parsing/output-recovery.d.ts +4 -2
  33. package/dist/runtime/parsing/output-recovery.js +30 -264
  34. package/dist/runtime/prompts/runtime-prompts.d.ts +1 -0
  35. package/dist/runtime/prompts/runtime-prompts.js +1 -0
  36. package/dist/scaffold/init-project.js +6 -4
  37. package/dist/tools.js +25 -2
  38. package/package.json +1 -1
package/README.md CHANGED
@@ -1195,6 +1195,7 @@ ACP transport notes:
1195
1195
  - ACP transport validation now covers the reference-client core flow: capability discovery, request submit, session lookup, request lookup, invalid-JSON handling, notification calls without response ids, stdio JSON-RPC, and HTTP plus SSE runtime notifications.
1196
1196
  - Cross-protocol conformance now has an explicit regression gate as well: ACP submission, A2A task lookup and continuation, and runtime MCP inspection must all project the same persisted `sessionId` / `requestId` runtime records instead of drifting into surface-specific identifiers or side stores.
1197
1197
  - For the thinnest editor or CLI starter, begin with `agent-harness acp serve --workspace . --transport stdio` and mirror the `examples/03_protocol-surfaces/app/acp-stdio/main.mjs` wire shape. Applications that want an in-process reference client can use `createAcpStdioClient(...)` to issue JSON-RPC requests and route runtime notifications without hand-rolling line parsing.
1198
+ - For the shortest local operator workflow, `botbotgo start --workspace .` now starts ACP, A2A, runtime MCP, and AG-UI together as managed local services, and `botbotgo stop --workspace .` stops the same set. `--services acp,mcp` narrows the set when one workspace only needs part of the local protocol surface.
1198
1199
  - When a local ACP HTTP endpoint should stay up beyond one foreground shell, use `botbotgo acp start --workspace . --host 127.0.0.1 --port 8787` and later `botbotgo acp stop --workspace .`. The CLI records the managed process under `.botbotgo/services/` so local operators can start and stop it without a second process manager.
1199
1200
  - `botbotgo` is the shortest local terminal entrypoint for a published install. It defaults the workspace to the current directory, so teams can `cd` into their own runtime workspace and run `botbotgo` or `botbotgo "..."` directly. `agent-harness` keeps the same implicit-chat behavior, and `-w/--workspace` still lets one shell point at another workspace when needed.
1200
1201
  - `agent-harness chat --workspace .` still acts as a local terminal shell over the shared `HarnessClient` contract when you want the explicit subcommand form: the default `stdio` path runs directly in-process against the workspace runtime, while `--transport http --host <host> --port <port>` can still target an already-running ACP HTTP endpoint. One-shot use can pass `--message`, while interactive mode supports `/context`, `/new`, `/agent <agentId>`, `/sessions`, `/requests`, `/resume <sessionId>`, `/approvals`, `/approve`, `/reject`, `/cancel`, `/events`, `/trace`, `/health`, `/overview`, `/session`, `/request [requestId]`, and `/exit`. `/resume` now validates the target session, restores its latest request id, and rehydrates the active agent context; `/request <requestId>` can switch the active request context to a persisted request; `/new` clears the current session/request context without leaving the shell. Terminal chat now keeps one operator-facing flow: it streams tool and progress events as they happen and still prints the full final answer, instead of switching to a separate request-tree-only mode.
@@ -1205,7 +1206,7 @@ ACP transport notes:
1205
1206
  - The interactive prompt now carries the live `agent`, `session`, and short `request` identifier together, so after each reply and during shell-history navigation the user stays anchored to the current runtime turn instead of dropping back to a bare input prompt.
1206
1207
  - `serveA2aHttp(runtime)` exposes an A2A-compatible HTTP JSON-RPC bridge plus agent card discovery, mapping both existing methods such as `message/send` and A2A v1.0 PascalCase methods such as `SendMessage`, `SendStreamingMessage`, `GetTask`, `ListTasks`, `CancelTask`, `SubscribeToTask`, `GetAgentCard`, `GetExtendedAgentCard`, and task push-notification config methods onto the existing session/request runtime surface. The bridge now advertises both `1.0` and `0.3` JSON-RPC interfaces, answers `HEAD` / `OPTIONS` discovery on the agent-card path, sets supported-version discovery headers, can optionally expose registry URLs plus detached signed-card metadata for surrounding discovery systems, validates `A2A-Version`, records `A2A-Extensions` into runtime invocation metadata, publishes `TASK_STATE_*` statuses plus the `{ task }` `SendMessage` wrapper, streams an initial `{ task }` snapshot plus later `{ statusUpdate }` payloads over SSE for v1 streaming methods, and can send best-effort webhook task snapshots for configured push notification receivers.
1207
1208
  - For a managed local bridge, use `botbotgo a2a start --workspace . --host 127.0.0.1 --port 8080` and later `botbotgo a2a stop --workspace .`. This uses the same `.botbotgo/services/` state directory as ACP so both local protocol bridges follow one terminal-first workflow.
1208
- - `serveAgUiHttp(runtime)` exposes an AG-UI-compatible HTTP SSE bridge that projects runtime lifecycle, safe progress commentary, text output, upstream thinking, step progress, and tool calls onto `RUN_*`, `STATUS_UPDATE`, `TEXT_MESSAGE_*`, `THINKING_TEXT_MESSAGE_*`, `STEP_*`, and `TOOL_CALL_*` events for UI clients.
1209
+ - `serveAgUiHttp(runtime)` exposes an AG-UI-compatible HTTP SSE bridge that projects runtime lifecycle, safe progress commentary, text output, upstream thinking, step progress, and tool calls onto `RUN_*`, `STATUS_UPDATE`, `TEXT_MESSAGE_*`, `THINKING_TEXT_MESSAGE_*`, `STEP_*`, and `TOOL_CALL_*` events for UI clients. `botbotgo ag-ui start|stop` now manages that HTTP bridge in the same workspace-local service registry as ACP, A2A, and runtime MCP.
1209
1210
  - `createRuntimeMcpServer(runtime)`, `serveRuntimeMcpOverStdio(runtime)`, and `serveRuntimeMcpOverStreamableHttp(runtime)` expose the persisted runtime control surface itself as MCP tools, including sessions, requests, approvals, artifacts, events, and package export helpers. `botbotgo mcp serve --transport streamable-http --host 127.0.0.1 --port 8090` serves the same tool surface over Streamable HTTP, and `botbotgo mcp start|stop` manages that background endpoint for one workspace.
1210
1211
  - `listRequestEvents(...)`, `listRequestTraceItems(...)`, and `exportRequestPackage(...)` are the request-first inspection helpers.
1211
1212
  - `exportRequestPackage(...)` and `exportSessionPackage(...)` package stable runtime records, transcript, approvals, events, artifacts, and governance evidence for operator tooling without reaching into persistence internals.
package/README.zh.md CHANGED
@@ -1161,6 +1161,7 @@ ACP transport 说明:
1161
1161
  - ACP transport 现已覆盖核心参考客户端流程验证:capability discovery、request submit、session lookup、request lookup、invalid JSON 处理、无 id notification 不返回响应,以及 stdio JSON-RPC 与 HTTP + SSE runtime notifications。
1162
1162
  - 现在还额外有一条跨协议一致性回归门:ACP 发起的 request、A2A 读取或继续的 task,以及 runtime MCP 暴露的检查结果,必须始终指向同一组持久化 `sessionId` / `requestId` 运行时记录,不能漂移成各协议各自维护的标识体系。
1163
1163
  - 如果要从最薄的一层 editor / CLI starter 开始,优先用 `agent-harness acp serve --workspace . --transport stdio`,并直接参考 `examples/03_protocol-surfaces/app/acp-stdio/main.mjs` 的 wire shape。需要在应用内使用 reference client 时,可直接用 `createAcpStdioClient(...)` 发起 JSON-RPC 请求并分流 runtime notifications,避免每个 sidecar 自己重写 line parsing。
1164
+ - 如果想用最短的本地运维命令把整套协议面一起拉起,现在可以直接运行 `botbotgo start --workspace .`,它会把 ACP、A2A、runtime MCP 与 AG-UI 一起作为受管本地服务启动;`botbotgo stop --workspace .` 会按同一组服务统一停止。若只需要其中一部分,可用 `--services acp,mcp` 收窄启动集合。
1164
1165
  - 如果本地 ACP HTTP endpoint 需要在前台 shell 之外持续运行,可使用 `botbotgo acp start --workspace . --host 127.0.0.1 --port 8787`,之后再用 `botbotgo acp stop --workspace .` 停止。CLI 会把受管进程记录到 `.botbotgo/services/` 下,因此本地运维不必再额外引入第二套进程管理器。
1165
1166
  - 对发布后的安装包来说,`botbotgo` 是最短的本地终端入口。它会默认把当前目录当作 workspace,所以团队只需要 `cd` 到自己的 runtime workspace,然后直接运行 `botbotgo` 或 `botbotgo "..."`。`agent-harness` 也保留同样的隐式 chat 行为;如果要从同一个 shell 指向别的 workspace,继续用 `-w/--workspace` 即可。
1166
1167
  - `agent-harness chat --workspace .` 则继续保留为显式子命令形式的本地终端壳:默认 `stdio` 路径会直接在当前进程里连接 workspace runtime,而 `--transport http --host <host> --port <port>` 仍然可以直连已运行的 ACP HTTP endpoint。一次性调用可用 `--message`,交互模式则支持 `/context`、`/new`、`/agent <agentId>`、`/sessions`、`/requests`、`/resume <sessionId>`、`/approvals`、`/approve`、`/reject`、`/cancel`、`/events`、`/trace`、`/health`、`/overview`、`/session`、`/request [requestId]` 与 `/exit`。`/resume` 现在会先校验目标 session,恢复它的 latest request id,并同步 active agent context;`/request <requestId>` 可以把当前上下文切到某个已持久化 request;`/new` 则会在不退出 shell 的情况下清空当前 session/request context。终端 chat 现在统一走一条面向 operator 的输出路径:tool 和进度事件会实时打印,最终完整答复也会继续输出,而不再切到单独的 request-tree-only 模式。
@@ -1171,7 +1172,7 @@ ACP transport 说明:
1171
1172
  - 交互式 prompt 现在会同时显示当前 `agent`、`session` 和短 `request` 标识,因此每轮回复之后以及使用 shell 历史记录时,用户都还能清楚地看到自己正处在哪个 runtime turn 上,而不会掉回一个没有上下文的裸输入提示。
1172
1173
  - `serveA2aHttp(runtime)` 提供 A2A HTTP JSON-RPC bridge 与 agent card discovery,同时兼容 `message/send` 这类旧方法,以及 `SendMessage`、`SendStreamingMessage`、`GetTask`、`ListTasks`、`CancelTask`、`SubscribeToTask`、`GetAgentCard`、`GetExtendedAgentCard` 与 task push-notification config 这类 A2A v1.0 方法,并统一映射到现有 session/request 运行记录。bridge 现在会同时声明 `1.0` 与 `0.3` 两个 JSON-RPC interface、在 agent card 路径上响应 `HEAD` / `OPTIONS` discovery、写出支持版本的 discovery headers、可选暴露 registry URL 与 detached signed-card metadata 供外围发现系统使用、校验 `A2A-Version`、把 `A2A-Extensions` 记录进 runtime invocation metadata、发布 `TASK_STATE_*` 状态与 `SendMessage` 的 `{ task }` wrapper、在 v1 streaming 方法上先通过 SSE 输出 `{ task }` 初始快照,再输出 `{ statusUpdate }` 增量状态,并可向已配置的 webhook receiver 发送 best-effort task push notifications。
1173
1174
  - 如果要把本地 A2A bridge 作为受管后台服务运行,可使用 `botbotgo a2a start --workspace . --host 127.0.0.1 --port 8080`,之后再用 `botbotgo a2a stop --workspace .` 停止。它与 ACP 共用 `.botbotgo/services/` 状态目录,因此两个本地协议桥都可以沿用同一套终端优先运维流程。
1174
- - `serveAgUiHttp(runtime)` 提供 AG-UI HTTP SSE bridge,把 runtime 生命周期、安全进度播报、文本输出、upstream thinking、step 进度与 tool call 投影成 `RUN_*`、`STATUS_UPDATE`、`TEXT_MESSAGE_*`、`THINKING_TEXT_MESSAGE_*`、`STEP_*` 与 `TOOL_CALL_*` 事件,便于 UI 客户端直接接入。
1175
+ - `serveAgUiHttp(runtime)` 提供 AG-UI HTTP SSE bridge,把 runtime 生命周期、安全进度播报、文本输出、upstream thinking、step 进度与 tool call 投影成 `RUN_*`、`STATUS_UPDATE`、`TEXT_MESSAGE_*`、`THINKING_TEXT_MESSAGE_*`、`STEP_*` 与 `TOOL_CALL_*` 事件,便于 UI 客户端直接接入。`botbotgo ag-ui start|stop` 现在也会把这条 HTTP bridge 托管进与 ACP、A2A、runtime MCP 相同的 workspace 本地服务注册表中。
1175
1176
  - `createRuntimeMcpServer(runtime)`、`serveRuntimeMcpOverStdio(runtime)` 与 `serveRuntimeMcpOverStreamableHttp(runtime)` 会把持久化 runtime 控制面本身暴露成 MCP tools,包括 sessions、requests、approvals、artifacts、events 与 package export helpers。`botbotgo mcp serve --transport streamable-http --host 127.0.0.1 --port 8090` 会把同一套控制面作为 Streamable HTTP 暴露出去,而 `botbotgo mcp start|stop` 可直接托管该后台 endpoint。
1176
1177
  - `listRequestEvents(...)`、`listRequestTraceItems(...)` 与 `exportRequestPackage(...)` 是 request-first 的检查 helper。
1177
1178
  - `exportRequestPackage(...)` 与 `exportSessionPackage(...)` 可把稳定 runtime 记录、transcript、approvals、events、artifacts 与 governance evidence 一起打包给管理工具,而不必直接访问 persistence 内部实现。
package/dist/cli/main.js CHANGED
@@ -20,7 +20,7 @@ import { probeChatWorkspace, readChatWorkspaceModelInfo, } from "./chat-workspac
20
20
  import { handleA2aCommand, handleAcpCommand, handleAgUiCommand, handleMcpCommand } from "./server-commands.js";
21
21
  import { handleRuntimeCommand } from "./runtime-commands.js";
22
22
  import { defaultIsManagedProcessRunning, defaultSignalManagedProcess, defaultSpawnManagedCliProcess, sleep, } from "./managed-service.js";
23
- import { isTopLevelCliCommand, parseChatOptions, parseInitOptions, renderUsage, } from "./options.js";
23
+ import { isTopLevelCliCommand, parseChatOptions, parseInitOptions, parseTopLevelManagedServiceOptions, renderUsage, } from "./options.js";
24
24
  import { resolveCliWorkspaceRoot, validateCliWorkspaceRoot, } from "./workspace.js";
25
25
  export { renderChatBanner, renderChatPromptLine } from "./chat-ui.js";
26
26
  export { probeChatWorkspace, renderChatRuntimeFailure } from "./chat-workspace.js";
@@ -258,6 +258,79 @@ export async function runCli(argv, io = {}, deps = {}) {
258
258
  }
259
259
  return executeChat(parsed);
260
260
  }
261
+ const runManagedServiceLifecycle = async (lifecycle, rawArgs) => {
262
+ const parsed = parseTopLevelManagedServiceOptions(rawArgs);
263
+ if (parsed.error) {
264
+ stderr(`${parsed.error}\n`);
265
+ stderr(renderUsage());
266
+ return 1;
267
+ }
268
+ const startOrder = ["acp", "a2a", "mcp", "ag-ui"];
269
+ const stopOrder = ["ag-ui", "mcp", "a2a", "acp"];
270
+ const serviceOrder = lifecycle === "start" ? startOrder : stopOrder;
271
+ const selectedServices = serviceOrder.filter((service) => parsed.services.includes(service));
272
+ for (const service of selectedServices) {
273
+ const commandArgs = [
274
+ lifecycle,
275
+ "--workspace",
276
+ parsed.workspaceRoot ?? ".",
277
+ ];
278
+ if (lifecycle === "start" && parsed.hostname) {
279
+ commandArgs.push("--host", parsed.hostname);
280
+ }
281
+ const resolvedPort = service === "acp"
282
+ ? parsed.acpPort
283
+ : service === "a2a"
284
+ ? parsed.a2aPort
285
+ : service === "mcp"
286
+ ? parsed.mcpPort
287
+ : parsed.agUiPort;
288
+ if (lifecycle === "start" && typeof resolvedPort === "number") {
289
+ commandArgs.push("--port", String(resolvedPort));
290
+ }
291
+ const exitCode = service === "acp"
292
+ ? await handleAcpCommand(commandArgs, { cwd, stderr }, {
293
+ createHarness,
294
+ serveAcp,
295
+ serveAcpHttp,
296
+ spawnManagedCliProcess,
297
+ isManagedProcessRunning,
298
+ signalManagedProcess,
299
+ serveA2a,
300
+ serveAgUi,
301
+ serveRuntimeMcp,
302
+ serveRuntimeMcpStreamableHttp,
303
+ })
304
+ : service === "a2a"
305
+ ? await handleA2aCommand(commandArgs, { cwd, stderr }, {
306
+ createHarness,
307
+ serveA2a,
308
+ spawnManagedCliProcess,
309
+ isManagedProcessRunning,
310
+ signalManagedProcess,
311
+ })
312
+ : service === "mcp"
313
+ ? await handleMcpCommand(commandArgs, { cwd, stderr }, {
314
+ createHarness,
315
+ serveRuntimeMcp,
316
+ serveRuntimeMcpStreamableHttp,
317
+ spawnManagedCliProcess,
318
+ isManagedProcessRunning,
319
+ signalManagedProcess,
320
+ })
321
+ : await handleAgUiCommand(commandArgs, { cwd, stderr }, {
322
+ createHarness,
323
+ serveAgUi,
324
+ spawnManagedCliProcess,
325
+ isManagedProcessRunning,
326
+ signalManagedProcess,
327
+ });
328
+ if (exitCode !== 0) {
329
+ return exitCode;
330
+ }
331
+ }
332
+ return 0;
333
+ };
261
334
  if (!isTopLevelCliCommand(command)) {
262
335
  const parsed = parseChatOptions(argv);
263
336
  if (parsed.error) {
@@ -267,6 +340,9 @@ export async function runCli(argv, io = {}, deps = {}) {
267
340
  }
268
341
  return executeChat(parsed);
269
342
  }
343
+ if (command === "start" || command === "stop") {
344
+ return runManagedServiceLifecycle(command, [projectName, ...rest].filter((item) => typeof item === "string"));
345
+ }
270
346
  if (command === "acp") {
271
347
  return handleAcpCommand([projectName, ...rest], { cwd, stderr }, {
272
348
  createHarness,
@@ -282,7 +358,13 @@ export async function runCli(argv, io = {}, deps = {}) {
282
358
  });
283
359
  }
284
360
  if (command === "ag-ui") {
285
- return handleAgUiCommand([projectName, ...rest], { cwd, stderr }, { createHarness, serveAgUi });
361
+ return handleAgUiCommand([projectName, ...rest], { cwd, stderr }, {
362
+ createHarness,
363
+ serveAgUi,
364
+ spawnManagedCliProcess,
365
+ isManagedProcessRunning,
366
+ signalManagedProcess,
367
+ });
286
368
  }
287
369
  if (command === "a2a") {
288
370
  return handleA2aCommand([projectName, ...rest], { cwd, stderr }, {
@@ -1,4 +1,4 @@
1
- export type ManagedCliService = "acp" | "a2a" | "mcp";
1
+ export type ManagedCliService = "acp" | "a2a" | "mcp" | "ag-ui";
2
2
  export type ManagedCliServiceState = {
3
3
  pid: number;
4
4
  service: ManagedCliService;
@@ -16,6 +16,7 @@ export declare function writeManagedServiceState(state: ManagedCliServiceState):
16
16
  export declare function clearManagedServiceState(workspaceRoot: string, service: ManagedCliService): Promise<void>;
17
17
  export declare function defaultIsManagedProcessRunning(pid: number): boolean;
18
18
  export declare function defaultSignalManagedProcess(pid: number, signal?: NodeJS.Signals): void;
19
+ export declare function resolveManagedCliNodeExecutable(cwd: string, env?: NodeJS.ProcessEnv, currentNodePath?: string, currentNodeVersion?: string): string;
19
20
  export declare function defaultSpawnManagedCliProcess(input: {
20
21
  args: string[];
21
22
  cwd: string;
@@ -1,6 +1,7 @@
1
1
  import { spawn } from "node:child_process";
2
- import { existsSync, openSync } from "node:fs";
2
+ import { existsSync, openSync, readFileSync, readdirSync } from "node:fs";
3
3
  import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
4
+ import { homedir } from "node:os";
4
5
  import path from "node:path";
5
6
  import { fileURLToPath } from "node:url";
6
7
  function resolveManagedServiceRoot(workspaceRoot) {
@@ -44,11 +45,86 @@ export function defaultIsManagedProcessRunning(pid) {
44
45
  export function defaultSignalManagedProcess(pid, signal) {
45
46
  process.kill(pid, signal);
46
47
  }
48
+ function parseRequestedNodeMajor(cwd) {
49
+ const nodeVersionPath = path.join(cwd, ".node-version");
50
+ if (existsSync(nodeVersionPath)) {
51
+ const value = readFileSync(nodeVersionPath, "utf8").trim();
52
+ const major = Number.parseInt(value.replace(/^v/i, ""), 10);
53
+ if (Number.isInteger(major) && major > 0) {
54
+ return major;
55
+ }
56
+ }
57
+ const packageJsonPath = path.join(cwd, "package.json");
58
+ if (!existsSync(packageJsonPath)) {
59
+ return undefined;
60
+ }
61
+ try {
62
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
63
+ const match = packageJson.engines?.node?.match(/>=\s*(\d+)/);
64
+ if (!match) {
65
+ return undefined;
66
+ }
67
+ const major = Number.parseInt(match[1] ?? "", 10);
68
+ return Number.isInteger(major) && major > 0 ? major : undefined;
69
+ }
70
+ catch {
71
+ return undefined;
72
+ }
73
+ }
74
+ function parseNodeMajorFromExecutable(nodePath) {
75
+ const match = nodePath.match(/\/v(\d+)\.\d+\.\d+\/bin\/node$/);
76
+ if (!match) {
77
+ return undefined;
78
+ }
79
+ const major = Number.parseInt(match[1] ?? "", 10);
80
+ return Number.isInteger(major) && major > 0 ? major : undefined;
81
+ }
82
+ function compareNodeVersionDirNames(left, right) {
83
+ const leftParts = left.replace(/^v/i, "").split(".").map((part) => Number.parseInt(part, 10) || 0);
84
+ const rightParts = right.replace(/^v/i, "").split(".").map((part) => Number.parseInt(part, 10) || 0);
85
+ const length = Math.max(leftParts.length, rightParts.length);
86
+ for (let index = 0; index < length; index += 1) {
87
+ const leftValue = leftParts[index] ?? 0;
88
+ const rightValue = rightParts[index] ?? 0;
89
+ if (leftValue !== rightValue) {
90
+ return rightValue - leftValue;
91
+ }
92
+ }
93
+ return 0;
94
+ }
95
+ export function resolveManagedCliNodeExecutable(cwd, env = process.env, currentNodePath = process.execPath, currentNodeVersion = process.versions.node) {
96
+ const requestedMajor = parseRequestedNodeMajor(cwd);
97
+ if (!requestedMajor) {
98
+ return currentNodePath;
99
+ }
100
+ const currentMajor = Number.parseInt(currentNodeVersion.split(".")[0] ?? "", 10);
101
+ if (currentMajor === requestedMajor) {
102
+ return currentNodePath;
103
+ }
104
+ const home = env.HOME ?? env.USERPROFILE ?? homedir();
105
+ const versionsRoot = path.join(home, ".nvm", "versions", "node");
106
+ if (!existsSync(versionsRoot)) {
107
+ return currentNodePath;
108
+ }
109
+ const preferredVersion = readdirSync(versionsRoot, { withFileTypes: true })
110
+ .filter((entry) => entry.isDirectory() && entry.name.startsWith(`v${requestedMajor}.`))
111
+ .map((entry) => entry.name)
112
+ .sort(compareNodeVersionDirNames)[0];
113
+ if (!preferredVersion) {
114
+ return currentNodePath;
115
+ }
116
+ const preferredNodePath = path.join(versionsRoot, preferredVersion, "bin", "node");
117
+ if (!existsSync(preferredNodePath)) {
118
+ return currentNodePath;
119
+ }
120
+ const preferredMajor = parseNodeMajorFromExecutable(preferredNodePath);
121
+ return preferredMajor === requestedMajor ? preferredNodePath : currentNodePath;
122
+ }
47
123
  export async function defaultSpawnManagedCliProcess(input) {
48
124
  await mkdir(path.dirname(input.stdoutPath), { recursive: true });
49
125
  const stdoutFd = openSync(input.stdoutPath, "a");
50
126
  const stderrFd = openSync(input.stderrPath, "a");
51
- const child = spawn(process.execPath, [fileURLToPath(import.meta.url), ...input.args], {
127
+ const child = spawn(resolveManagedCliNodeExecutable(input.cwd), [fileURLToPath(import.meta.url), ...input.args], {
52
128
  cwd: input.cwd,
53
129
  detached: true,
54
130
  stdio: ["ignore", stdoutFd, stderrFd],
@@ -1,3 +1,15 @@
1
+ declare const TOP_LEVEL_MANAGED_SERVICES: readonly ["acp", "a2a", "mcp", "ag-ui"];
2
+ export type TopLevelManagedService = (typeof TOP_LEVEL_MANAGED_SERVICES)[number];
3
+ export declare function parseTopLevelManagedServiceOptions(args: string[]): {
4
+ workspaceRoot?: string;
5
+ services: TopLevelManagedService[];
6
+ hostname?: string;
7
+ acpPort?: number;
8
+ a2aPort?: number;
9
+ mcpPort?: number;
10
+ agUiPort?: number;
11
+ error?: string;
12
+ };
1
13
  export declare function parseAcpServeOptions(args: string[], defaultTransport?: "stdio" | "http"): {
2
14
  workspaceRoot?: string;
3
15
  transport: "stdio" | "http";
@@ -22,3 +34,4 @@ export declare function parseRuntimeMcpServeOptions(args: string[], defaultTrans
22
34
  port?: number;
23
35
  error?: string;
24
36
  };
37
+ export {};
@@ -1,3 +1,119 @@
1
+ const TOP_LEVEL_MANAGED_SERVICES = ["acp", "a2a", "mcp", "ag-ui"];
2
+ export function parseTopLevelManagedServiceOptions(args) {
3
+ let workspaceRoot;
4
+ let hostname;
5
+ let services;
6
+ let acpPort;
7
+ let a2aPort;
8
+ let mcpPort;
9
+ let agUiPort;
10
+ const parsePort = (value, label) => {
11
+ if (!value) {
12
+ return { error: `Missing value for ${label}` };
13
+ }
14
+ const parsedPort = Number.parseInt(value, 10);
15
+ if (!Number.isFinite(parsedPort) || parsedPort < 0) {
16
+ return { error: `Invalid ${label} value: ${value}` };
17
+ }
18
+ return parsedPort;
19
+ };
20
+ for (let index = 0; index < args.length; index += 1) {
21
+ const arg = args[index];
22
+ if (arg === "--workspace" || arg === "-w") {
23
+ const value = args[index + 1];
24
+ if (!value) {
25
+ return { services: services ?? [...TOP_LEVEL_MANAGED_SERVICES], hostname, acpPort, a2aPort, mcpPort, agUiPort, error: "Missing value for --workspace" };
26
+ }
27
+ workspaceRoot = value;
28
+ index += 1;
29
+ continue;
30
+ }
31
+ if (arg === "--services") {
32
+ const value = args[index + 1];
33
+ if (!value) {
34
+ return { services: services ?? [...TOP_LEVEL_MANAGED_SERVICES], workspaceRoot, hostname, acpPort, a2aPort, mcpPort, agUiPort, error: "Missing value for --services" };
35
+ }
36
+ const parsedServices = value
37
+ .split(",")
38
+ .map((item) => item.trim())
39
+ .filter(Boolean);
40
+ if (parsedServices.length === 0) {
41
+ return { services: services ?? [...TOP_LEVEL_MANAGED_SERVICES], workspaceRoot, hostname, acpPort, a2aPort, mcpPort, agUiPort, error: "Missing value for --services" };
42
+ }
43
+ const invalidService = parsedServices.find((service) => !TOP_LEVEL_MANAGED_SERVICES.includes(service));
44
+ if (invalidService) {
45
+ return {
46
+ services: services ?? [...TOP_LEVEL_MANAGED_SERVICES],
47
+ workspaceRoot,
48
+ hostname,
49
+ acpPort,
50
+ a2aPort,
51
+ mcpPort,
52
+ agUiPort,
53
+ error: `Unsupported managed service: ${invalidService}`,
54
+ };
55
+ }
56
+ services = Array.from(new Set(parsedServices));
57
+ index += 1;
58
+ continue;
59
+ }
60
+ if (arg === "--host") {
61
+ const value = args[index + 1];
62
+ if (!value) {
63
+ return { services: services ?? [...TOP_LEVEL_MANAGED_SERVICES], workspaceRoot, acpPort, a2aPort, mcpPort, agUiPort, error: "Missing value for --host" };
64
+ }
65
+ hostname = value;
66
+ index += 1;
67
+ continue;
68
+ }
69
+ if (arg === "--acp-port") {
70
+ const parsedPort = parsePort(args[index + 1], "--acp-port");
71
+ if (typeof parsedPort !== "number") {
72
+ return { services: services ?? [...TOP_LEVEL_MANAGED_SERVICES], workspaceRoot, hostname, acpPort, a2aPort, mcpPort, agUiPort, error: parsedPort.error };
73
+ }
74
+ acpPort = parsedPort;
75
+ index += 1;
76
+ continue;
77
+ }
78
+ if (arg === "--a2a-port") {
79
+ const parsedPort = parsePort(args[index + 1], "--a2a-port");
80
+ if (typeof parsedPort !== "number") {
81
+ return { services: services ?? [...TOP_LEVEL_MANAGED_SERVICES], workspaceRoot, hostname, acpPort, a2aPort, mcpPort, agUiPort, error: parsedPort.error };
82
+ }
83
+ a2aPort = parsedPort;
84
+ index += 1;
85
+ continue;
86
+ }
87
+ if (arg === "--mcp-port") {
88
+ const parsedPort = parsePort(args[index + 1], "--mcp-port");
89
+ if (typeof parsedPort !== "number") {
90
+ return { services: services ?? [...TOP_LEVEL_MANAGED_SERVICES], workspaceRoot, hostname, acpPort, a2aPort, mcpPort, agUiPort, error: parsedPort.error };
91
+ }
92
+ mcpPort = parsedPort;
93
+ index += 1;
94
+ continue;
95
+ }
96
+ if (arg === "--ag-ui-port") {
97
+ const parsedPort = parsePort(args[index + 1], "--ag-ui-port");
98
+ if (typeof parsedPort !== "number") {
99
+ return { services: services ?? [...TOP_LEVEL_MANAGED_SERVICES], workspaceRoot, hostname, acpPort, a2aPort, mcpPort, agUiPort, error: parsedPort.error };
100
+ }
101
+ agUiPort = parsedPort;
102
+ index += 1;
103
+ continue;
104
+ }
105
+ return { services: services ?? [...TOP_LEVEL_MANAGED_SERVICES], workspaceRoot, hostname, acpPort, a2aPort, mcpPort, agUiPort, error: `Unknown option: ${arg}` };
106
+ }
107
+ return {
108
+ workspaceRoot,
109
+ services: services ?? [...TOP_LEVEL_MANAGED_SERVICES],
110
+ hostname,
111
+ acpPort,
112
+ a2aPort,
113
+ mcpPort,
114
+ agUiPort,
115
+ };
116
+ }
1
117
  export function parseAcpServeOptions(args, defaultTransport = "stdio") {
2
118
  let workspaceRoot;
3
119
  let transport = defaultTransport;
@@ -1,5 +1,5 @@
1
1
  export declare function renderUsage(): string;
2
2
  export declare function isTopLevelCliCommand(value?: string): boolean;
3
3
  export { parseChatOptions, parseInitOptions } from "./options-init-chat.js";
4
- export { parseAcpServeOptions, parseHttpServeOptions, parseRuntimeMcpServeOptions, parseWorkspaceOnlyOptions, } from "./options-serve.js";
4
+ export { parseAcpServeOptions, parseHttpServeOptions, parseTopLevelManagedServiceOptions, parseRuntimeMcpServeOptions, parseWorkspaceOnlyOptions, } from "./options-serve.js";
5
5
  export { parseRuntimeExportOptions, parseRuntimeInspectOptions, parseScheduledRunOptions, } from "./options-runtime.js";
@@ -4,6 +4,8 @@ export function renderUsage() {
4
4
  agent-harness chat [-w <path>] [--transport stdio|http] [--host <hostname>] [--port <port>] [--agent <agentId>] [--session <sessionId>] [--message <text>]
5
5
  agent-harness [-w <path>] [prompt]
6
6
  botbotgo [-w <path>] [prompt]
7
+ agent-harness start [-w <path>] [--services acp,a2a,mcp,ag-ui] [--host <hostname>] [--acp-port <port>] [--a2a-port <port>] [--mcp-port <port>] [--ag-ui-port <port>]
8
+ agent-harness stop [-w <path>] [--services acp,a2a,mcp,ag-ui]
7
9
  agent-harness acp serve [--workspace <path>] [--transport stdio|http] [--host <hostname>] [--port <port>]
8
10
  agent-harness acp start [--workspace <path>] [--host <hostname>] [--port <port>]
9
11
  agent-harness acp stop [--workspace <path>]
@@ -11,6 +13,8 @@ export function renderUsage() {
11
13
  agent-harness a2a start [--workspace <path>] [--host <hostname>] [--port <port>]
12
14
  agent-harness a2a stop [--workspace <path>]
13
15
  agent-harness ag-ui serve [--workspace <path>] [--host <hostname>] [--port <port>]
16
+ agent-harness ag-ui start [--workspace <path>] [--host <hostname>] [--port <port>]
17
+ agent-harness ag-ui stop [--workspace <path>]
14
18
  agent-harness runtime overview [--workspace <path>] [--limit <n>] [--json]
15
19
  agent-harness runtime health [--workspace <path>] [--json]
16
20
  agent-harness runtime approvals list [--workspace <path>] [--status <pending|approved|edited|rejected|expired>] [--json]
@@ -35,6 +39,8 @@ Chat defaults to the current directory as the workspace. Use -w to point at anot
35
39
  export function isTopLevelCliCommand(value) {
36
40
  return value === "init"
37
41
  || value === "chat"
42
+ || value === "start"
43
+ || value === "stop"
38
44
  || value === "acp"
39
45
  || value === "a2a"
40
46
  || value === "ag-ui"
@@ -43,5 +49,5 @@ export function isTopLevelCliCommand(value) {
43
49
  || value === "runtime-mcp";
44
50
  }
45
51
  export { parseChatOptions, parseInitOptions } from "./options-init-chat.js";
46
- export { parseAcpServeOptions, parseHttpServeOptions, parseRuntimeMcpServeOptions, parseWorkspaceOnlyOptions, } from "./options-serve.js";
52
+ export { parseAcpServeOptions, parseHttpServeOptions, parseTopLevelManagedServiceOptions, parseRuntimeMcpServeOptions, parseWorkspaceOnlyOptions, } from "./options-serve.js";
47
53
  export { parseRuntimeExportOptions, parseRuntimeInspectOptions, parseScheduledRunOptions, } from "./options-runtime.js";
@@ -30,7 +30,7 @@ type ServerDeps = ManagedDeps & {
30
30
  serveRuntimeMcpStreamableHttp: typeof serveRuntimeMcpOverStreamableHttp;
31
31
  };
32
32
  export declare function handleAcpCommand(subcommandAndArgs: string[], io: CommandIo, deps: ServerDeps): Promise<number>;
33
- export declare function handleAgUiCommand(subcommandAndArgs: string[], io: CommandIo, deps: Pick<ServerDeps, "createHarness" | "serveAgUi">): Promise<number>;
33
+ export declare function handleAgUiCommand(subcommandAndArgs: string[], io: CommandIo, deps: Pick<ServerDeps, "createHarness" | "serveAgUi"> & ManagedDeps): Promise<number>;
34
34
  export declare function handleA2aCommand(subcommandAndArgs: string[], io: CommandIo, deps: Pick<ServerDeps, "createHarness" | "serveA2a"> & ManagedDeps): Promise<number>;
35
35
  export declare function handleMcpCommand(subcommandAndArgs: string[], io: CommandIo, deps: Pick<ServerDeps, "createHarness" | "serveRuntimeMcp" | "serveRuntimeMcpStreamableHttp"> & ManagedDeps): Promise<number>;
36
36
  export {};
@@ -85,26 +85,46 @@ export async function handleAcpCommand(subcommandAndArgs, io, deps) {
85
85
  export async function handleAgUiCommand(subcommandAndArgs, io, deps) {
86
86
  const [subcommand, ...subcommandArgs] = subcommandAndArgs;
87
87
  const { cwd, stderr } = io;
88
- const { createHarness, serveAgUi } = deps;
89
- if (subcommand !== "serve") {
88
+ const { createHarness, serveAgUi, spawnManagedCliProcess, isManagedProcessRunning, signalManagedProcess } = deps;
89
+ if (subcommand !== "serve" && subcommand !== "start" && subcommand !== "stop") {
90
90
  stderr(renderUsage());
91
91
  return 1;
92
92
  }
93
- const parsed = parseHttpServeOptions(subcommandArgs, "AG-UI");
94
- if (parsed.error) {
95
- stderr(`${parsed.error}\n`);
93
+ const parsedStop = subcommand === "stop" ? parseWorkspaceOnlyOptions(subcommandArgs) : undefined;
94
+ const parsedServe = subcommand !== "stop" ? parseHttpServeOptions(subcommandArgs, "AG-UI") : undefined;
95
+ const parseError = parsedStop?.error ?? parsedServe?.error;
96
+ if (parseError) {
97
+ stderr(`${parseError}\n`);
96
98
  stderr(renderUsage());
97
99
  return 1;
98
100
  }
99
101
  try {
100
- const workspacePath = resolveValidatedWorkspace(cwd, parsed.workspaceRoot, stderr);
102
+ const workspaceRoot = parsedStop?.workspaceRoot ?? parsedServe?.workspaceRoot;
103
+ const workspacePath = resolveValidatedWorkspace(cwd, workspaceRoot, stderr);
101
104
  if (!workspacePath) {
102
105
  return 1;
103
106
  }
107
+ if (subcommand === "start") {
108
+ const args = ["ag-ui", "serve", "--workspace", workspacePath];
109
+ if (parsedServe?.hostname) {
110
+ args.push("--host", parsedServe.hostname);
111
+ }
112
+ if (typeof parsedServe?.port === "number") {
113
+ args.push("--port", String(parsedServe.port));
114
+ }
115
+ return startManagedHttpService({ service: "ag-ui", workspacePath, args, stderr, spawnManagedCliProcess, isManagedProcessRunning });
116
+ }
117
+ if (subcommand === "stop") {
118
+ return stopManagedHttpService({ service: "ag-ui", workspacePath, stderr, isManagedProcessRunning, signalManagedProcess });
119
+ }
120
+ if (!parsedServe) {
121
+ stderr(renderUsage());
122
+ return 1;
123
+ }
104
124
  const runtime = await createHarness(workspacePath);
105
125
  const server = await serveAgUi(runtime, {
106
- hostname: parsed.hostname,
107
- port: parsed.port,
126
+ hostname: parsedServe.hostname,
127
+ port: parsedServe.port,
108
128
  });
109
129
  stderr(`Serving AG-UI over http from ${workspacePath} at ${server.runUrl}\n`);
110
130
  await server.completed;
@@ -50,7 +50,8 @@ spec:
50
50
  Rules:
51
51
  - Store only durable reusable knowledge. Reject transient chatter, scratchpad, or duplication without added value.
52
52
  - Reject raw request/session summaries, source-specific page/news recaps, and generic "we learned how to use the tools/workflow" reflections unless they clearly contain reusable preferences, facts, decisions, or procedures.
53
- - If transcript evidence shows the user explicitly asked the system to remember or follow a future instruction and the assistant confirmed that intent, store the durable instruction instead of rejecting it as a generic summary.
53
+ - Never store assistant- or system-authored workflow instructions inferred from a single transcript reflection, such as "how the system should investigate", "what the assistant should ask first", or other generic control-flow recipes for future turns.
54
+ - If transcript evidence shows the user explicitly asked the system to remember or follow a future instruction and the assistant confirmed that intent, store the durable user instruction instead of rejecting it as a generic summary.
54
55
  - Treat durable knowledge as generic mutable records with database-like operations over the same underlying knowledge item.
55
56
  - One candidate may yield zero, one, or multiple durable knowledge items. Split it only when the input clearly contains multiple independently mutable knowledge points.
56
57
  - When storing a knowledge item, always return a `knowledgeMutation` object with a stable `identity` and an `operation` of `create`, `update`, or `delete`.
@@ -59,7 +60,7 @@ spec:
59
60
  - If an existing relevant record already represents the same underlying knowledge item, reuse that record's `knowledge_identity` instead of inventing a new one.
60
61
  - Do not invent a second identity just because the new statement negates, revokes, deletes, or replaces the old wording. That is usually the same knowledge item with a different mutation operation.
61
62
  - The stored `content` must be canonical knowledge text, not an assistant acknowledgement such as "已记住" or "I will remember".
62
- - You may optionally include `operationalRule` when the knowledge is naturally a rule, instruction, or recurring procedure. Treat it as structured metadata, not as the primary identity mechanism.
63
+ - You may optionally include `operationalRule` when the knowledge is a durable user-approved rule, instruction, or recurring procedure. Do not use it for assistant-only workflow guidance inferred from one conversation. Treat it as structured metadata, not as the primary identity mechanism.
63
64
  - Prefer semantic/episodic/procedural kinds only.
64
65
  - Prefer scopes session/agent/workspace/user/project only.
65
66
  - If the candidate should not be stored, return {"store": false, "reason": "..."}
@@ -31,6 +31,10 @@ mark completed steps as `completed`, keep the active step as `in_progress`, mark
31
31
  short `result` summaries when they help the user follow progress. Use descriptive todo content that names the
32
32
  real step; never use placeholders like `1`, `2`, `3`, `step 1`, or `todo 1`.
33
33
 
34
+ For local troubleshooting and investigation requests, default the initial scope to the current workspace and the
35
+ active shell/runtime context. Start by inspecting the available evidence yourself instead of asking the user to
36
+ restate the problem, choose the first diagnostic branch, or manually provide the obvious starting context.
37
+
34
38
  For workspace file operations, always use workspace-relative paths such as `tmp-counter.txt` or `docs/index.html`.
35
39
  Do not use absolute host paths like `/tmp/...` or `/Users/...`. Use `write_file` only for the initial creation of
36
40
  a file. After a file exists, switch to `read_file` plus `edit_file` for updates instead of repeating `write_file`.
@@ -81,7 +81,8 @@ spec:
81
81
  Rules:
82
82
  - Store only durable reusable knowledge. Reject transient chatter, scratchpad, or duplication without added value.
83
83
  - Reject raw request/session summaries, source-specific page/news recaps, and generic "we learned how to use the tools/workflow" reflections unless they clearly contain reusable preferences, facts, decisions, or procedures.
84
- - If transcript evidence shows the user explicitly asked the system to remember or follow a future instruction and the assistant confirmed that intent, store the durable instruction instead of rejecting it as a generic summary.
84
+ - Never store assistant- or system-authored workflow instructions inferred from a single transcript reflection, such as "how the system should investigate", "what the assistant should ask first", or other generic control-flow recipes for future turns.
85
+ - If transcript evidence shows the user explicitly asked the system to remember or follow a future instruction and the assistant confirmed that intent, store the durable user instruction instead of rejecting it as a generic summary.
85
86
  - Treat durable knowledge as generic mutable records with database-like operations over the same underlying knowledge item.
86
87
  - One candidate may yield zero, one, or multiple durable knowledge items. Split it only when the input clearly contains multiple independently mutable knowledge points.
87
88
  - When storing a knowledge item, always return a `knowledgeMutation` object with a stable `identity` and an `operation` of `create`, `update`, or `delete`.
@@ -90,7 +91,7 @@ spec:
90
91
  - If an existing relevant record already represents the same underlying knowledge item, reuse that record's `knowledge_identity` instead of inventing a new one.
91
92
  - Do not invent a second identity just because the new statement negates, revokes, deletes, or replaces the old wording. That is usually the same knowledge item with a different mutation operation.
92
93
  - The stored `content` must be canonical knowledge text, not an assistant acknowledgement such as "已记住" or "I will remember".
93
- - You may optionally include `operationalRule` when the knowledge is naturally a rule, instruction, or recurring procedure. Treat it as structured metadata, not as the primary identity mechanism.
94
+ - You may optionally include `operationalRule` when the knowledge is a durable user-approved rule, instruction, or recurring procedure. Do not use it for assistant-only workflow guidance inferred from one conversation. Treat it as structured metadata, not as the primary identity mechanism.
94
95
  - Prefer semantic/episodic/procedural kinds only.
95
96
  - Prefer scopes session/agent/workspace/user/project only.
96
97
  - If the candidate should not be stored, return {"store": false, "reason": "..."}
@@ -2,7 +2,7 @@ import { createPersistentId } from "../utils/id.js";
2
2
  import { findMemoryRecordById, getMemoryRecord, listMemoryRecordsForScopes, persistStructuredMemoryRecords, removeMemoryRecord, updateMemoryRecord, } from "../runtime/harness/system/runtime-memory-records.js";
3
3
  import { consolidateStructuredMemoryScope } from "../runtime/harness/system/runtime-memory-consolidation.js";
4
4
  import { renderMemoryCandidatesMarkdown } from "../runtime/harness/system/runtime-memory-candidates.js";
5
- import { shouldRecallDurableMemory, normalizeLangMemMemoryKind, shouldStoreMemoryCandidate, } from "../runtime/harness/system/runtime-memory-policy.js";
5
+ import { shouldRecallDurableMemory, normalizeLangMemMemoryKind, shouldStoreMemoryCandidate, isNonDurableTranscriptWorkflowMemory, } from "../runtime/harness/system/runtime-memory-policy.js";
6
6
  const ALL_SCOPES = ["session", "agent", "workspace", "user", "project"];
7
7
  const TITLE_BY_SCOPE = {
8
8
  session: "Session Structured Memory",
@@ -499,6 +499,7 @@ export class DefaultKnowledgeModule {
499
499
  includeStale: input.includeStale === true,
500
500
  context: normalizedContext,
501
501
  }))
502
+ .filter(({ record }) => !isNonDurableTranscriptWorkflowMemory(record))
502
503
  .map((item) => {
503
504
  const scopeBoost = item.record.scope === "session"
504
505
  ? 4
@@ -1,2 +1,2 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.323";
2
- export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-21";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.324";
2
+ export declare const AGENT_HARNESS_RELEASE_DATE = "2026-04-22";
@@ -1,2 +1,2 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.323";
2
- export const AGENT_HARNESS_RELEASE_DATE = "2026-04-21";
1
+ export const AGENT_HARNESS_VERSION = "0.0.324";
2
+ export const AGENT_HARNESS_RELEASE_DATE = "2026-04-22";