dsh-a2a-server 0.2.0 → 0.4.0

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 (92) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/CHANGELOG.zh-CN.md +39 -0
  3. package/README.md +122 -29
  4. package/README.zh-CN.md +91 -28
  5. package/cordis.patch.yml +10 -6
  6. package/docker/Dockerfile +5 -0
  7. package/docs/visitor-isolation.en.md +51 -0
  8. package/docs/visitor-isolation.md +75 -0
  9. package/lib/briefing.d.ts +39 -0
  10. package/lib/briefing.d.ts.map +1 -0
  11. package/lib/briefing.js +108 -0
  12. package/lib/briefing.js.map +1 -0
  13. package/lib/config.d.ts +58 -15
  14. package/lib/config.d.ts.map +1 -1
  15. package/lib/config.js +74 -5
  16. package/lib/config.js.map +1 -1
  17. package/lib/contexts.d.ts +9 -4
  18. package/lib/contexts.d.ts.map +1 -1
  19. package/lib/contexts.js +7 -13
  20. package/lib/contexts.js.map +1 -1
  21. package/lib/docker-executor.d.ts +11 -0
  22. package/lib/docker-executor.d.ts.map +1 -0
  23. package/lib/docker-executor.js +115 -0
  24. package/lib/docker-executor.js.map +1 -0
  25. package/lib/index.d.ts +1 -31
  26. package/lib/index.d.ts.map +1 -1
  27. package/lib/index.js +462 -440
  28. package/lib/index.js.map +1 -1
  29. package/lib/isolation.d.ts +21 -0
  30. package/lib/isolation.d.ts.map +1 -0
  31. package/lib/isolation.js +108 -0
  32. package/lib/isolation.js.map +1 -0
  33. package/lib/management.d.ts +5 -0
  34. package/lib/management.d.ts.map +1 -0
  35. package/lib/management.js +75 -0
  36. package/lib/management.js.map +1 -0
  37. package/lib/microsandbox-executor.d.ts +39 -0
  38. package/lib/microsandbox-executor.d.ts.map +1 -0
  39. package/lib/microsandbox-executor.js +316 -0
  40. package/lib/microsandbox-executor.js.map +1 -0
  41. package/lib/microsandbox-runtime.d.ts +28 -0
  42. package/lib/microsandbox-runtime.d.ts.map +1 -0
  43. package/lib/microsandbox-runtime.js +164 -0
  44. package/lib/microsandbox-runtime.js.map +1 -0
  45. package/lib/projection.d.ts +46 -3
  46. package/lib/projection.d.ts.map +1 -1
  47. package/lib/projection.js +19 -8
  48. package/lib/projection.js.map +1 -1
  49. package/lib/router.d.ts +47 -0
  50. package/lib/router.d.ts.map +1 -1
  51. package/lib/router.js +218 -65
  52. package/lib/router.js.map +1 -1
  53. package/lib/sandbox-files.d.ts +16 -0
  54. package/lib/sandbox-files.d.ts.map +1 -0
  55. package/lib/sandbox-files.js +168 -0
  56. package/lib/sandbox-files.js.map +1 -0
  57. package/lib/sandbox-lifecycle.d.ts +48 -0
  58. package/lib/sandbox-lifecycle.d.ts.map +1 -0
  59. package/lib/sandbox-lifecycle.js +125 -0
  60. package/lib/sandbox-lifecycle.js.map +1 -0
  61. package/lib/security.d.ts +4 -2
  62. package/lib/security.d.ts.map +1 -1
  63. package/lib/security.js +4 -2
  64. package/lib/security.js.map +1 -1
  65. package/lib/state.d.ts +37 -0
  66. package/lib/state.d.ts.map +1 -0
  67. package/lib/state.js +146 -0
  68. package/lib/state.js.map +1 -0
  69. package/lib/tasks.d.ts +4 -0
  70. package/lib/tasks.d.ts.map +1 -1
  71. package/lib/tasks.js +18 -0
  72. package/lib/tasks.js.map +1 -1
  73. package/lib/types.d.ts +9 -0
  74. package/lib/types.d.ts.map +1 -1
  75. package/lib/types.js.map +1 -1
  76. package/lib/version.d.ts +1 -1
  77. package/lib/version.js +1 -1
  78. package/lib/visitor-read.d.ts +4 -0
  79. package/lib/visitor-read.d.ts.map +1 -0
  80. package/lib/visitor-read.js +20 -0
  81. package/lib/visitor-read.js.map +1 -0
  82. package/lib/visitor-worker.d.ts +2 -0
  83. package/lib/visitor-worker.d.ts.map +1 -0
  84. package/lib/visitor-worker.js +18 -0
  85. package/lib/visitor-worker.js.map +1 -0
  86. package/lib/visitor-worker.mjs +14686 -0
  87. package/lib/visitor-worker.py +78 -0
  88. package/lib/workspace-read.d.ts +9 -0
  89. package/lib/workspace-read.d.ts.map +1 -0
  90. package/lib/workspace-read.js +152 -0
  91. package/lib/workspace-read.js.map +1 -0
  92. package/package.json +48 -31
package/cordis.patch.yml CHANGED
@@ -7,12 +7,15 @@
7
7
  # REQUIRES ctx.webServer, which ships in `dsh-web-app`, NOT in `dsh-base`. On a
8
8
  # profile without it this row loads but stays PENDING (Cordis holds a plugin
9
9
  # whose injected services are absent), so nothing serves and nothing errors.
10
+ #
11
+ # NOTE: keep this file plain YAML — the installer's package validation parses
12
+ # it with a strict loader that rejects `!!js` expressions, even though the
13
+ # runtime overlay loader accepts them.
10
14
  - insert:
11
15
  - id: a2a-server
12
16
  name: dsh-a2a-server
13
17
  config:
14
18
  basePath: /a2a
15
- protocolVersion: '0.3.0'
16
19
  card:
17
20
  name: dsh-harness
18
21
  description: A DeepSeek Harness agent reachable over A2A.
@@ -20,11 +23,12 @@
20
23
  skills: []
21
24
  peers: {}
22
25
  isolation:
23
- # Default posture is isolation: each peer gets its own working
24
- # directory, which also makes the cwd-equality authority in
25
- # cross-session tooling separate peers for free. A deployment whose
26
- # peers collaborate on one repository must say so explicitly.
26
+ # Each peer gets its own working directory. This is not a filesystem
27
+ # sandbox: read_file separately restricts reads to the selected root,
28
+ # or to a peerWorkspaces override. Other visitor tools are disabled.
29
+ # This placeholder is overridden by any real deployment layer (the
30
+ # console's overlay sets it per agent); supply an absolute path.
27
31
  workspaceMode: per-peer
28
- workspaceRoot: !!js `${process.env.DSH_HOME ?? process.env.HOME}/a2a-workspaces`
32
+ workspaceRoot: /srv/dsh/a2a
29
33
  push:
30
34
  enabled: false
@@ -0,0 +1,5 @@
1
+ FROM node:22-bookworm-slim
2
+ RUN apt-get update && apt-get install -y --no-install-recommends python3 \
3
+ && rm -rf /var/lib/apt/lists/*
4
+ USER 1000:1000
5
+ WORKDIR /work
@@ -0,0 +1,51 @@
1
+ # Per-visitor sandboxes (0.4.0)
2
+
3
+ Portal defaults to local Microsandbox 0.7.11 microVMs. DSH remains the trusted orchestrator for authentication, model requests and conversation storage. Only read_file is exposed to visitors today; Python, shell and write tools are not automatically enabled. Real Python tests verify private read/write capability for future tool integrations.
4
+
5
+ ## Automatic preparation
6
+
7
+ Click Start in Portal. It checks/upgrades the plugin, downloads a pinned SDK and a digest-pinned Python image, and reports progress. Components are cached in the user directory; no manual Microsandbox or Docker installation is needed. Plain chat creates no visitor VM. The first tool call creates one; later calls reuse it.
8
+
9
+ Supported targets are Windows x64/ARM64 with WHP, Linux x64/ARM64 with KVM, and Apple Silicon macOS with ARM64 Node.js. Node.js 22+ is required. Only Windows x64 has been tested locally; macOS and Linux still need acceptance testing. Intel Macs are unsupported.
10
+
11
+ Enabling Windows Hypervisor Platform may require a user action and restart. Linux requires access to /dev/kvm. Administrator privileges, firmware settings and OS restarts cannot be silently arranged. Preparation failures are visible and retryable; tools never fall back to host execution.
12
+
13
+ Portal installs dsh-a2a-server@^0.4.0 from npm. For local development, build the dsh-a2a checkout with pnpm install and pnpm build, then set A2A_PLUGIN_SOURCE to its absolute path in the terminal launching Portal. The npm release does not need this override. Standalone configurations select isolation.execution: microsandbox. Existing configs without that field retain read-only mode, which has no VM boundary. The docker backend remains available for compatibility.
14
+
15
+ ## File boundaries and persistence
16
+
17
+ | Data | Inside the VM | Local persistence |
18
+ | --- | --- | --- |
19
+ | Selected public data | /source, read-only copy | Original host directory is never mounted |
20
+ | Current visitor files | /work, read/write | Exclusive persistent virtual disk |
21
+ | Other visitors | Not mounted | Separate disks |
22
+ | Sessions, protocol state, credentials | Not mounted | Trusted backend storage |
23
+
24
+ Public data includes .hop/handoff.md. Before each tool call it is validated and copied; changes stop that visitor VM before replacing its public projection, then restart with the same private disk. Trees are capped at 512 MiB and 100000 entries, so large datasets increase tool latency. Microsandbox public projections exclude entries named node_modules at any depth, case-insensitively, including dependency links. Their contents are never traversed; the original project's dependencies are neither moved nor deleted. Other symlinks, junctions, hard links, special files and nested filesystems remain rejected. Private workspace imports and Docker raw mounts do not use this exclusion and still reject all links.
25
+
26
+ Portal stores VM mappings at ~/.a2a/workspaces/<agent>/.microsandbox/<peer>/record.json and read-only public projections beside them. Existing <peer>/ files are imported on first boot and retained as migration sources. New /work files persist in the virtual disk; they are **not synchronized back to the old host folder**. A generated-file download/export interface has not been added.
27
+
28
+ The SDK, native state and VM disks live under ~/.a2a/sandbox-runtime, overridable with A2A_SANDBOX_HOME before startup. Back up both the private mapping directory and runtime state. Do not delete either or run destroy/remove on user VMs; those actions delete disks. Removing a peer credential does not automatically delete its files.
29
+
30
+ Identity is agent + authenticated peer, not conversation or message. Multiple contexts for one peer share a VM and private files. People sharing a token share this identity; issue independent credentials.
31
+
32
+ ## Lifecycle and quotas
33
+
34
+ After the last tool finishes, 30 idle seconds pause the VM; 10 idle minutes stop it. Pausing retains memory. Stopping releases runtime resources and keeps disks. New tool calls resume a paused VM or restart a stopped VM with its original disk. Active or queued work prevents idle transitions, and calls for one visitor are serialized.
35
+
36
+ Each backend allows four resident VMs including paused ones. At capacity, the oldest idle VM stops early; if every VM is busy, new tool work is rejected. Normal backend shutdown stops its VMs. A new controller reclaims recorded instances abandoned by a previous controller, without destroying disks. Abrupt power loss or forced termination can still lose unflushed writes.
37
+
38
+ VMs have one CPU, 512 MiB RAM, a 256 MiB root disk and 512 MiB private disk. Networking is disabled. Tools run as UID 1000 with a 30-second execution timeout and 256 KB output cap. The public projection is read-only/noexec/nosuid/nodev; private disks are nosuid/nodev. The host user, hypervisor and pinned image remain trusted; this is not an absolute defense against virtualization vulnerabilities.
39
+
40
+ read_file defaults to public data; root: workspace selects the current visitor's disk. Text reads remain bounded to UTF-8 files up to 2 MiB, 32000 returned characters and 500 lines/entries per call. Backend conversation records are not tool-readable.
41
+
42
+ ## Migration and verification
43
+
44
+ Stop the old agent and start with the new Portal. It copies current visitors' legacy directories outside public data, verifies the copy and removes the old source. Conflicting destinations are never overwritten. First VM boot imports that visitor's private files and refuses conflicting content. Archive private leftovers for removed visitors outside public data manually.
45
+
46
+ Automatic public briefings still update .hop/handoff.md and appear in Portal. Trusted owner maintenance runs on the host, outside the visitor sandbox.
47
+
48
+ Run pnpm typecheck, pnpm test, pnpm test:package and pnpm test:microsandbox. The last command automatically prepares real microVMs and synthetic files to exercise public mutation denial, private CRUD, peer separation, link attacks, network/credential isolation, cancellation, lifecycle, persistence and abandoned-instance recovery.
49
+
50
+ Set A2A_MSB_WALLCLOCK=1 for a full ten-minute wall-clock lifecycle test. Otherwise controlled time triggers actual native VM transitions. A2A_MSB_TEST_RUNTIME optionally reuses a test runtime cache. Legacy Docker validation remains pnpm test:docker and requires a local Linux Docker engine plus the image built from docker/Dockerfile.
51
+
@@ -0,0 +1,75 @@
1
+ # 访客独立沙箱(0.4.0)
2
+
3
+ Portal 默认使用 Microsandbox 0.7.11,本机运行微型虚拟机。DSH 后端负责认证、模型调用和聊天记录;工具运行在访客沙箱中。当前模型仍只开放 `read_file`,没有自动开放 Python、Shell 或写入工具。私有目录读写能力已经由真实 Python 测试验证,可供以后受控开放的工具使用。
4
+
5
+ ## 启动体验
6
+
7
+ 点击 Portal「启动」即可自动检查/更新插件、下载固定版本的沙箱组件和固定摘要的 Python 镜像,页面显示进度。组件安装到用户目录并缓存,不需要手动安装 Microsandbox 或 Docker。纯聊天不创建访客 VM;首次工具调用才创建,后续调用复用。
8
+
9
+ 支持的宿主环境:Windows x64/ARM64 + WHP、Linux x64/ARM64 + KVM、Apple Silicon macOS + ARM64 Node.js;Node.js 22 以上。当前只在 Windows x64 实测,其他平台需要单独验收。Intel Mac 暂不支持。
10
+
11
+ Windows 未启用“Windows 虚拟机监控程序平台”时,需要用户启用系统功能并重启。Linux 需要 /dev/kvm 访问权限。管理员权限、BIOS 设置和系统重启无法静默代办。准备失败会显示原因,重试复用已下载内容,绝不退回宿主机执行工具。
12
+
13
+ Portal 使用 npm 插件 `dsh-a2a-server@^0.4.0`。仅在测试本地源码时:
14
+
15
+ ```powershell
16
+ # D:\code\dsh-a2a
17
+ pnpm install
18
+ pnpm build
19
+ # 在启动 Portal 的同一个终端里指定源码包位置
20
+ $env:A2A_PLUGIN_SOURCE='D:\code\dsh-a2a'
21
+ # 然后启动 Talk2MyAgent Portal
22
+ ```
23
+
24
+ 使用 npm 版本无需设置这个开发变量。独立 DSH 配置使用 `isolation.execution: microsandbox`;旧配置未填写时仍是 `read-only`,仅有受限读取器,没有 VM 边界。`docker` 作为兼容选项保留。
25
+
26
+ ## 目录与持久化
27
+
28
+ | 内容 | 沙箱可见范围 | 本地保存 |
29
+ | --- | --- | --- |
30
+ | 用户指定公共资料 | /source,只读副本 | 原始目录不挂入 VM;包括公开简报 .hop/handoff.md |
31
+ | 当前访客的文件 | /work,可增加、修改、重命名、删除 | 每个访客独享持久化虚拟磁盘 |
32
+ | 其他访客的文件 | 不挂载 | 各自独立磁盘 |
33
+ | 聊天记录、协议状态、凭据 | 不挂载 | 可信 DSH 后端独立保存 |
34
+
35
+ Portal 的私有根在 `~/.a2a/workspaces/<agent>`。其 `.microsandbox/<peer>/record.json` 保存访客到 VM 的稳定映射,`public/` 保存公共资料副本;原有 `<peer>/` 文件作为首次导入源保留。以后 /work 中的新文件保存在虚拟磁盘中,**不会同步回旧的宿主机普通文件夹**。
36
+
37
+ 运行组件与所有 VM 磁盘默认保存在 `~/.a2a/sandbox-runtime`;可在启动前通过 `A2A_SANDBOX_HOME` 指定。备份需同时包含私有根的映射和 runtime 的 state 目录。不要清除这些目录,或对用户 VM 执行 destroy/remove;这些操作会删除磁盘。用户生成文件的独立下载/导出入口尚未增加。
38
+
39
+ 公共资料会在工具调用前验证并复制;内容更新时停止该访客旧 VM、更新只读副本,然后以原私有磁盘启动。复制最多 512 MiB、100000 项,大目录会增加调用耗时。Microsandbox 的公共资料副本自动排除各层级的 `node_modules`(不区分大小写),包括同名依赖链接;不会遍历其内容,也不会移动或删除原项目的依赖。其他硬链接、符号链接/junction、特殊文件和嵌套文件系统仍被拒绝,避免通过别名访问宿主机其他资料。私有工作区导入和 Docker 原目录挂载不使用此排除规则,仍拒绝所有链接。
40
+
41
+ 隔离单位是 **agent + 已认证 peer**,不是每轮消息或 contextId。不同聊天共享同一 peer 的文件;多人共用访客令牌也会共用沙箱。每人应分配独立凭据。删除凭据不会自动删除旧文件。
42
+
43
+ ## 生命周期
44
+
45
+ - 第一次调用工具:创建该访客 VM;聊天本身不创建。
46
+ - 工具完成后空闲 30 秒:暂停,保留进程和内存;暂停不释放主要内存。
47
+ - 自最后一次工具完成空闲 10 分钟:停止 VM,释放运行资源;磁盘与文件保留。
48
+ - 暂停期间来新工具请求:恢复原 VM;停止后再来:用原磁盘重新启动。
49
+ - 有工具执行或排队时不暂停;同一访客工具串行,不同访客各用自己的 VM。
50
+ - 每个后端最多 4 个驻留 VM(包括暂停的);满额先停止最久未使用的空闲 VM。都忙时拒绝新增工具,已有文件不丢失。
51
+ - 正常关闭后端会停止其 VM;控制进程意外退出后,下次启动回收其记录的遗留实例。不会销毁用户磁盘。机器断电/强杀与普通磁盘一样可能丢失未落盘写入。
52
+
53
+ 每个 VM 为 1 CPU、512 MiB 内存、256 MiB 根磁盘、512 MiB 私有工作盘,无网络,工具以 UID 1000 运行,单次执行限时 30 秒、输出 256 KB。公共副本只读且 noexec/nosuid/nodev;私有盘 nosuid/nodev。宿主用户、虚拟化实现与受信镜像仍属于可信边界,这不是对虚拟化漏洞的绝对保证。
54
+
55
+ `read_file({"path":"info.txt"})` 读公共资料;`read_file({"root":"workspace","path":"."})` 读自己的工作区。文件限 UTF-8 文本、2 MiB、单次 32000 字符与 500 行/项。后端聊天记录不作为可读工具目录。
56
+
57
+ ## 旧数据迁移
58
+
59
+ 停止旧 agent,再用新版 Portal 启动。Portal 把公共目录中的当前访客旧目录复制到独立私有根,校验后移除旧目录;目标已存在则拒绝覆盖。首次 VM 启动再导入自己的私有文件,冲突拒绝覆盖。已删除访客留下的历史私有文件需要主人移出公共资料目录。
60
+
61
+ 公开简报继续默认定时更新到 .hop/handoff.md 并显示在 Portal。主人简报任务是可信后端任务,仍在宿主机执行,不属于访客沙箱。
62
+
63
+ ## 测试
64
+
65
+ ```sh
66
+ pnpm typecheck
67
+ pnpm test
68
+ pnpm test:package
69
+ pnpm test:microsandbox
70
+ ```
71
+
72
+ 最后一项会自动准备真实 Microsandbox,使用合成文件测试只读公共资料、私有 CRUD、跨访客隔离、链接攻击、网络/凭据隔离、取消、暂停/恢复/停止、重启和遗留实例回收。不会读取真实用户资料。
73
+
74
+ 设置 `A2A_MSB_WALLCLOCK=1` 再运行,可执行完整 10 分钟真实计时测试;否则通过可控时钟触发真实 VM 的生命周期转换。`A2A_MSB_TEST_RUNTIME` 可复用一个测试 runtime 缓存。Docker 兼容验证仍用 `pnpm test:docker`(需本地 Linux Docker 和 docker/Dockerfile 构建的镜像)。
75
+
@@ -0,0 +1,39 @@
1
+ export declare const BRIEFING_INTERVAL_MS: number;
2
+ interface LegacyBriefingScheduler {
3
+ catalog(): Promise<Array<{
4
+ id: string;
5
+ sessionId: string;
6
+ title: string;
7
+ prompt: string;
8
+ }>>;
9
+ delete(request: {
10
+ id: string;
11
+ sessionId: string;
12
+ }): Promise<unknown>;
13
+ }
14
+ /** Retire only the old Portal-generated job for this exact project. */
15
+ export declare function retireLegacyBriefing(scheduler: LegacyBriefingScheduler | undefined, root: string): Promise<void>;
16
+ export declare class AutomaticBriefing {
17
+ private readonly options;
18
+ private timer;
19
+ private closed;
20
+ private running;
21
+ private enabled;
22
+ private nextRunAt;
23
+ private readonly stateFile;
24
+ constructor(options: {
25
+ settingsFile: string;
26
+ stateDirectory: string;
27
+ run: () => Promise<void>;
28
+ warn: (message: string) => void;
29
+ now?: () => number;
30
+ });
31
+ private save;
32
+ private readEnabled;
33
+ /** Polling also observes an off/on switch without restarting the agent. */
34
+ tick(): Promise<void>;
35
+ start(): void;
36
+ stop(): void;
37
+ }
38
+ export {};
39
+ //# sourceMappingURL=briefing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"briefing.d.ts","sourceRoot":"","sources":["../src/briefing.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB,QAAiB,CAAA;AAGlD,UAAU,uBAAuB;IAC/B,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAA;IAC3F,MAAM,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACrE;AAED,uEAAuE;AACvE,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,uBAAuB,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUtH;AAED,qBAAa,iBAAiB;IAQhB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAPpC,OAAO,CAAC,KAAK,CAA4C;IACzD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,OAAO,CAAO;IACtB,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;gBAEL,OAAO,EAAE;QACpC,YAAY,EAAE,MAAM,CAAA;QACpB,cAAc,EAAE,MAAM,CAAA;QACtB,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;QACxB,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;QAC/B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;KACnB;IAWD,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,WAAW;IAWnB,2EAA2E;IACrE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB3B,KAAK,IAAI,IAAI;IAOb,IAAI,IAAI,IAAI;CAIb"}
@@ -0,0 +1,108 @@
1
+ /** One automatic briefing loop owned by the online agent, independent of Portal. */
2
+ import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ export const BRIEFING_INTERVAL_MS = 60 * 60 * 1000;
5
+ const CHECK_INTERVAL_MS = 5000;
6
+ /** Retire only the old Portal-generated job for this exact project. */
7
+ export async function retireLegacyBriefing(scheduler, root) {
8
+ if (!scheduler?.catalog || !scheduler.delete)
9
+ return;
10
+ const prefix = `【简报维护任务】受信任的项目范围只有一个:\n ${root}\n`;
11
+ for (const task of await scheduler.catalog()) {
12
+ if (task.title === '简报维护' && task.prompt.startsWith(prefix)
13
+ && (task.prompt.includes('4. 把简报写入当前工作目录的 handoff.draft.md')
14
+ || task.prompt.includes('4. 完整生成并检查上述内容后,直接覆盖写入当前工作目录的 handoff.md。'))) {
15
+ await scheduler.delete({ id: task.id, sessionId: task.sessionId });
16
+ }
17
+ }
18
+ }
19
+ export class AutomaticBriefing {
20
+ options;
21
+ timer;
22
+ closed = false;
23
+ running = false;
24
+ enabled = true;
25
+ nextRunAt = 0;
26
+ stateFile;
27
+ constructor(options) {
28
+ this.options = options;
29
+ mkdirSync(options.stateDirectory, { recursive: true, mode: 0o700 });
30
+ this.stateFile = join(options.stateDirectory, 'briefing-state.json');
31
+ try {
32
+ const state = JSON.parse(readFileSync(this.stateFile, 'utf8'));
33
+ if (typeof state.enabled !== 'boolean' || !Number.isSafeInteger(state.nextRunAt) || state.nextRunAt < 0)
34
+ throw new Error('invalid briefing state');
35
+ this.enabled = state.enabled;
36
+ this.nextRunAt = state.nextRunAt;
37
+ }
38
+ catch (error) {
39
+ if (error.code !== 'ENOENT')
40
+ throw error;
41
+ }
42
+ }
43
+ save() {
44
+ const temporary = `${this.stateFile}.tmp`;
45
+ try {
46
+ writeFileSync(temporary, JSON.stringify({ enabled: this.enabled, nextRunAt: this.nextRunAt }), { mode: 0o600 });
47
+ renameSync(temporary, this.stateFile);
48
+ }
49
+ finally {
50
+ rmSync(temporary, { force: true });
51
+ }
52
+ }
53
+ readEnabled() {
54
+ try {
55
+ const value = JSON.parse(readFileSync(this.options.settingsFile, 'utf8'));
56
+ if (typeof value.enabled !== 'boolean')
57
+ throw new Error('invalid automatic briefing setting');
58
+ return value.enabled;
59
+ }
60
+ catch (error) {
61
+ if (error.code === 'ENOENT')
62
+ return true;
63
+ throw error;
64
+ }
65
+ }
66
+ /** Polling also observes an off/on switch without restarting the agent. */
67
+ async tick() {
68
+ if (this.closed)
69
+ return;
70
+ try {
71
+ const enabled = this.readEnabled();
72
+ const now = this.options.now?.() ?? Date.now();
73
+ if (enabled !== this.enabled) {
74
+ this.enabled = enabled;
75
+ if (enabled)
76
+ this.nextRunAt = now;
77
+ this.save();
78
+ }
79
+ if (!enabled || this.running || now < this.nextRunAt)
80
+ return;
81
+ // Persist before paid work: restarting must not immediately repeat a run.
82
+ this.nextRunAt = now + BRIEFING_INTERVAL_MS;
83
+ this.save();
84
+ this.running = true;
85
+ try {
86
+ await this.options.run();
87
+ }
88
+ finally {
89
+ this.running = false;
90
+ }
91
+ }
92
+ catch (error) {
93
+ this.options.warn(`a2a: automatic briefing failed: ${String(error)}`);
94
+ }
95
+ }
96
+ start() {
97
+ if (this.closed || this.timer)
98
+ return;
99
+ this.timer = setInterval(() => { void this.tick(); }, CHECK_INTERVAL_MS);
100
+ this.timer.unref();
101
+ void this.tick();
102
+ }
103
+ stop() {
104
+ this.closed = true;
105
+ clearInterval(this.timer);
106
+ }
107
+ }
108
+ //# sourceMappingURL=briefing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"briefing.js","sourceRoot":"","sources":["../src/briefing.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACpF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAClD,MAAM,iBAAiB,GAAG,IAAI,CAAA;AAO9B,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,SAA8C,EAAE,IAAY;IACrG,IAAI,CAAC,SAAS,EAAE,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM;QAAE,OAAM;IACpD,MAAM,MAAM,GAAG,4BAA4B,IAAI,IAAI,CAAA;IACnD,KAAK,MAAM,IAAI,IAAI,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC;QAC7C,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;eACtD,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,kCAAkC,CAAC;mBACvD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,2CAA2C,CAAC,CAAC,EAAE,CAAC;YAC1E,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;QACpE,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,OAAO,iBAAiB;IAQC;IAPrB,KAAK,CAA4C;IACjD,MAAM,GAAG,KAAK,CAAA;IACd,OAAO,GAAG,KAAK,CAAA;IACf,OAAO,GAAG,IAAI,CAAA;IACd,SAAS,GAAG,CAAC,CAAA;IACJ,SAAS,CAAQ;IAElC,YAA6B,OAM5B;QAN4B,YAAO,GAAP,OAAO,CAMnC;QACC,SAAS,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QACnE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,qBAAqB,CAAC,CAAA;QACpE,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAA4C,CAAA;YACzG,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAA;YAClJ,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAA;YAC5B,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAA;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAAC,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAA;QAAC,CAAC;IACzF,CAAC;IAEO,IAAI;QACV,MAAM,SAAS,GAAG,GAAG,IAAI,CAAC,SAAS,MAAM,CAAA;QACzC,IAAI,CAAC;YACH,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;YAC/G,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;QACvC,CAAC;gBAAS,CAAC;YAAC,MAAM,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QAAC,CAAC;IAClD,CAAC;IAEO,WAAW;QACjB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAAyB,CAAA;YACjG,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;YAC7F,OAAO,KAAK,CAAC,OAAO,CAAA;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAA;YACnE,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,CAAC,MAAM;YAAE,OAAM;QACvB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;YAClC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAA;YAC9C,IAAI,OAAO,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;gBACtB,IAAI,OAAO;oBAAE,IAAI,CAAC,SAAS,GAAG,GAAG,CAAA;gBACjC,IAAI,CAAC,IAAI,EAAE,CAAA;YACb,CAAC;YACD,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS;gBAAE,OAAM;YAC5D,0EAA0E;YAC1E,IAAI,CAAC,SAAS,GAAG,GAAG,GAAG,oBAAoB,CAAA;YAC3C,IAAI,CAAC,IAAI,EAAE,CAAA;YACX,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;YACnB,IAAI,CAAC;gBAAC,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAA;YAAC,CAAC;oBAAS,CAAC;gBAAC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;YAAC,CAAC;QACnE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,mCAAmC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAAC,CAAC;IAC3F,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK;YAAE,OAAM;QACrC,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC,IAAI,EAAE,CAAA,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAA;QACvE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QAClB,KAAK,IAAI,CAAC,IAAI,EAAE,CAAA;IAClB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;QAClB,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC;CACF"}
package/lib/config.d.ts CHANGED
@@ -15,27 +15,21 @@ export interface PeerConfig {
15
15
  /** Credential reference name, NOT the token itself. */
16
16
  tokenEnv: string;
17
17
  }
18
- /**
19
- * How peers are kept from reaching each other.
20
- *
21
- * DEFERRED — a tool deny-list (`session_search`, `session_event_search`,
22
- * `session_trace`, `session_event_trace`, `session_event_read`, `list_agents`)
23
- * plus a named `unsafeAllowCrossSessionSearch` escape hatch for single-peer
24
- * deployments. Both were designed as defense in depth behind `workspaceMode`,
25
- * and neither is declared here until it is enforced: a security knob that
26
- * silently does nothing is worse than no knob at all.
27
- */
18
+ /** Working directories and the data roots exposed through the scoped reader. */
28
19
  export interface IsolationConfig {
29
20
  /**
30
- * `per-peer` gives each identity its own cwd, which also makes the existing
31
- * cwd-equality authority in cross-session tooling isolate them for free.
32
- * `shared` is the collaborative posture and must be chosen deliberately.
21
+ * Legacy field retained for checkpoint migration. Every visitor now has a
22
+ * private cwd outside public data, including old `shared` configurations.
33
23
  */
34
24
  workspaceMode: 'per-peer' | 'shared';
35
- /** Parent directory in `per-peer` mode; the common cwd in `shared` mode. */
25
+ /** Shared public data. Never the parent of new visitor workspaces. */
36
26
  workspaceRoot: string;
37
- /** Per-peer cwd overrides, e.g. pointing one trusted peer at a real repo. */
27
+ /** Legacy per-peer public-data overrides; no longer control runtime cwd. */
38
28
  peerWorkspaces: Record<string, string>;
29
+ /** Private runtime parent, disjoint from all public-data directories. */
30
+ privateRoot?: string;
31
+ /** Native mode only permits the bounded reader; isolated runtimes never fall back. */
32
+ execution?: 'read-only' | 'docker' | 'microsandbox';
39
33
  }
40
34
  /** One declared skill, as it appears on the Agent Card. */
41
35
  export interface SkillConfig {
@@ -82,6 +76,28 @@ export interface PushConfig {
82
76
  */
83
77
  enabled: boolean;
84
78
  }
79
+ /**
80
+ * The host's background briefing, injected when a visitor's context opens.
81
+ *
82
+ * The files are read once per Activation (best effort: an absent file is the
83
+ * normal "nothing to hand over" case) and merged into the context's FIRST
84
+ * visitor message rather than sent as their own turn — one model call is
85
+ * spared, and the turn counters stay about the visitor's messages only.
86
+ */
87
+ export interface PreambleConfig {
88
+ /** Publication-safe paths relative to the workspace root; absolute paths pass through. */
89
+ files: string[];
90
+ }
91
+ /** Local-only monitoring surface credentials. */
92
+ export interface AdminConfig {
93
+ /**
94
+ * Credential REFERENCE name holding the admin token, NOT the token itself.
95
+ * Empty or absent means the monitoring surface stays unmounted.
96
+ */
97
+ tokenEnv: string;
98
+ /** Local named pipe / Unix socket. Never served on the public HTTP carrier. */
99
+ socketPath?: string;
100
+ }
85
101
  /** The whole plugin configuration. */
86
102
  export interface A2AServerConfig {
87
103
  basePath: string;
@@ -103,7 +119,34 @@ export interface A2AServerConfig {
103
119
  blockTimeoutMs: number;
104
120
  contextIdleTtlMs: number;
105
121
  maxResidentContexts: number;
122
+ maxConcurrentTasks: number;
123
+ maxConcurrentTasksPerPeer: number;
124
+ maxTasksPerPeerPerDay: number;
125
+ taskTimeoutMs: number;
126
+ maxModelCallsPerTask: number;
127
+ maxOutputTokens: number;
128
+ maxInputChars: number;
129
+ maxOutputChars: number;
130
+ streamHeartbeatMs: number;
131
+ maxStreamsPerTask: number;
132
+ maxStreamBufferBytes: number;
133
+ storage: {
134
+ directory?: string;
135
+ retentionMs: number;
136
+ maxContexts: number;
137
+ maxContextBytes: number;
138
+ };
106
139
  isolation: IsolationConfig;
140
+ preamble: PreambleConfig;
141
+ /** Optional Portal-managed automatic updates; omission leaves the transport inert. */
142
+ briefing?: {
143
+ settingsFile: string;
144
+ prompt: string;
145
+ };
146
+ /**
147
+ * Enables owner RPC and monitoring on private local IPC, never public HTTP.
148
+ */
149
+ admin?: AdminConfig;
107
150
  push: PushConfig;
108
151
  }
109
152
  export declare const Config: Schema<A2AServerConfig>;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAQ7C,6DAA6D;AAC7D,MAAM,WAAW,UAAU;IACzB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,aAAa,EAAE,UAAU,GAAG,QAAQ,CAAA;IACpC,4EAA4E;IAC5E,aAAa,EAAE,MAAM,CAAA;IACrB,6EAA6E;IAC7E,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACvC;AAED,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,EAAE,CAAA;CACf;AAED,uCAAuC;AACvC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,iEAAiE;IACjE,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB;;;;;OAKG;IACH,cAAc,EAAE,WAAW,EAAE,CAAA;IAC7B,QAAQ,CAAC,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CACjD;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACjC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,eAAe,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,OAAO,GAAG,WAAW,CAAA;IAC/B,cAAc,EAAE,MAAM,CAAA;IACtB,gBAAgB,EAAE,MAAM,CAAA;IACxB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,SAAS,EAAE,eAAe,CAAA;IAC1B,IAAI,EAAE,UAAU,CAAA;CACjB;AAcD,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,eAAe,CAoDzC,CAAA;AAEF,oEAAoE;AACpE,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CA4BlE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/lib/config.js CHANGED
@@ -10,6 +10,7 @@
10
10
  * @module dsh-a2a/config
11
11
  */
12
12
  import Schema from '@deepseek-ai/schemastery';
13
+ import { isAbsolute } from 'node:path';
13
14
  /** A credential reference name: the shape `ctx.credentials` addresses. */
14
15
  const CREDENTIAL_REF = /^[A-Za-z_][A-Za-z0-9_]*$/;
15
16
  /** A peer name: also the workspace directory component, so no separators. */
@@ -20,10 +21,7 @@ const PEER_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
20
21
  * - `streamGranularity: 'message' | 'chunk'` — streaming currently emits only
21
22
  * committed assistant messages. The `chunk` mode needs an `assistant/chunk`
22
23
  * subscription and a decision about retried text reaching a peer.
23
- * - `taskTimeoutMs` — the orphan-task watchdog that would fail a task stuck
24
- * non-terminal. Nothing sweeps for those yet.
25
- *
26
- * Both were in the design; neither is wired. They return with their code.
24
+ * This option returns with its implementation.
27
25
  */
28
26
  export const Config = Schema.object({
29
27
  basePath: Schema.string().default('/a2a'),
@@ -57,18 +55,53 @@ export const Config = Schema.object({
57
55
  peers: Schema.dict(Schema.object({
58
56
  tokenEnv: Schema.string().required(),
59
57
  })).default({}),
60
- trustedPeers: Schema.array(Schema.string()),
58
+ // An array schema implicitly defaults to []; the wrapper preserves omission.
59
+ trustedPeers: Schema.union([Schema.array(Schema.string())]),
61
60
  rateLimitPerMinute: Schema.natural().default(60),
62
61
  maxContextTurns: Schema.natural().default(5),
63
62
  sendMode: Schema.union(['block', 'immediate']).default('block'),
64
63
  blockTimeoutMs: Schema.natural().default(60_000),
65
64
  contextIdleTtlMs: Schema.natural().default(1_800_000),
66
65
  maxResidentContexts: Schema.natural().default(64),
66
+ maxConcurrentTasks: Schema.natural().default(8),
67
+ maxConcurrentTasksPerPeer: Schema.natural().default(2),
68
+ maxTasksPerPeerPerDay: Schema.natural().default(200),
69
+ taskTimeoutMs: Schema.natural().default(120_000),
70
+ maxModelCallsPerTask: Schema.natural().default(8),
71
+ maxOutputTokens: Schema.natural().default(4096),
72
+ maxInputChars: Schema.natural().default(32_000),
73
+ maxOutputChars: Schema.natural().default(64_000),
74
+ streamHeartbeatMs: Schema.natural().default(15_000),
75
+ maxStreamsPerTask: Schema.natural().default(4),
76
+ maxStreamBufferBytes: Schema.natural().default(262_144),
77
+ storage: Schema.object({
78
+ directory: Schema.string(),
79
+ retentionMs: Schema.natural().default(86_400_000),
80
+ maxContexts: Schema.natural().default(256),
81
+ maxContextBytes: Schema.natural().default(4_000_000),
82
+ }),
67
83
  isolation: Schema.object({
68
84
  workspaceMode: Schema.union(['per-peer', 'shared']).default('per-peer'),
69
85
  workspaceRoot: Schema.string().required(),
70
86
  peerWorkspaces: Schema.dict(Schema.string()).default({}),
87
+ privateRoot: Schema.string(),
88
+ execution: Schema.union(['read-only', 'docker', 'microsandbox']).default('read-only'),
71
89
  }).required(),
90
+ preamble: Schema.object({
91
+ files: Schema.array(Schema.string()).default(['.hop/handoff.md']),
92
+ }),
93
+ briefing: Schema.object({
94
+ settingsFile: Schema.string().default(''),
95
+ prompt: Schema.string().default(''),
96
+ }),
97
+ // Schemastery instantiates a nested object even when the deployment omits
98
+ // it, so `required()` here would make every admin-less config fail at load.
99
+ // An empty tokenEnv instead reads as "not configured", and the coherence
100
+ // check below is what keeps a non-empty one a valid REFERENCE name.
101
+ admin: Schema.object({
102
+ tokenEnv: Schema.string().default(''),
103
+ socketPath: Schema.string(),
104
+ }),
72
105
  push: Schema.object({
73
106
  enabled: Schema.boolean().default(false),
74
107
  }),
@@ -90,7 +123,35 @@ export class A2AConfigError extends Error {
90
123
  * @throws {A2AConfigError} when a peer name, credential reference, or path is unusable.
91
124
  */
92
125
  export function assertConfigCoherent(config) {
126
+ if (config.isolation.privateRoot !== undefined && !isAbsolute(config.isolation.privateRoot))
127
+ throw new A2AConfigError('isolation.privateRoot must be absolute');
128
+ if (config.briefing?.prompt || config.briefing?.settingsFile) {
129
+ if (!config.admin?.tokenEnv || !config.briefing.prompt.trim() || !isAbsolute(config.briefing.settingsFile)) {
130
+ throw new A2AConfigError('automatic briefing requires owner administration, a prompt, and an absolute settingsFile');
131
+ }
132
+ }
133
+ for (const key of ['maxResidentContexts', 'maxConcurrentTasks', 'maxConcurrentTasksPerPeer',
134
+ 'maxTasksPerPeerPerDay', 'taskTimeoutMs', 'maxModelCallsPerTask', 'maxOutputTokens',
135
+ 'maxInputChars', 'maxOutputChars', 'streamHeartbeatMs', 'maxStreamsPerTask',
136
+ 'maxStreamBufferBytes']) {
137
+ if (!Number.isSafeInteger(config[key]) || config[key] <= 0)
138
+ throw new A2AConfigError(`${key} must be positive`);
139
+ }
140
+ for (const [key, value] of Object.entries(config.storage)) {
141
+ if (key !== 'directory' && (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0)) {
142
+ throw new A2AConfigError(`storage.${key} must be positive`);
143
+ }
144
+ }
145
+ if (config.publicUrl !== undefined) {
146
+ const url = new URL(config.publicUrl);
147
+ if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash) {
148
+ throw new A2AConfigError('publicUrl must be an HTTP(S) RPC URL without credentials, query, or fragment');
149
+ }
150
+ }
93
151
  for (const [peer, entry] of Object.entries(config.peers)) {
152
+ if (peer === 'owner') {
153
+ throw new A2AConfigError('peer name "owner" is reserved for the deployment owner (the local console)');
154
+ }
94
155
  if (!PEER_NAME.test(peer)) {
95
156
  throw new A2AConfigError(`peer name "${peer}" must match ${String(PEER_NAME)}: it becomes a workspace directory component`);
96
157
  }
@@ -108,6 +169,14 @@ export function assertConfigCoherent(config) {
108
169
  throw new A2AConfigError(`isolation.peerWorkspaces names "${peer}", which is not declared in peers`);
109
170
  }
110
171
  }
172
+ const adminTokenEnv = config.admin === undefined
173
+ || typeof config.admin.tokenEnv !== 'string'
174
+ || config.admin.tokenEnv.length === 0
175
+ ? undefined
176
+ : config.admin.tokenEnv;
177
+ if (adminTokenEnv !== undefined && !CREDENTIAL_REF.test(adminTokenEnv)) {
178
+ throw new A2AConfigError(`admin.tokenEnv must be a credential REFERENCE name (${String(CREDENTIAL_REF)}), not a token value`);
179
+ }
111
180
  if (!config.basePath.startsWith('/')) {
112
181
  throw new A2AConfigError(`basePath must start with "/": ${config.basePath}`);
113
182
  }
package/lib/config.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAEtC,0EAA0E;AAC1E,MAAM,cAAc,GAAG,0BAA0B,CAAA;AAEjD,6EAA6E;AAC7E,MAAM,SAAS,GAAG,6BAA6B,CAAA;AA0I/C;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,MAAM,GAA4B,MAAM,CAAC,MAAM,CAAC;IAC3D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IACzC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;IACzB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;IAEtB,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC;QAC5C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,8CAA8C,CAAC;QACpF,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;QACtC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;YACjC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAChC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;YACxC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;SAChD,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QACf,cAAc,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;YACzC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAChC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;YACxC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;SAChD,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QACf,4EAA4E;QAC5E,4EAA4E;QAC5E,wDAAwD;QACxD,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;YACtB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;YACzC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;SACjC,CAAC;KACH,CAAC;IAEF,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;QAC/B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KACrC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACf,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC3D,kBAAkB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAChD,eAAe,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAE5C,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,WAAW,CAAU,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IACxE,cAAc,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IAChD,gBAAgB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;IACrD,mBAAmB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IACjD,kBAAkB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,yBAAyB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,qBAAqB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;IACpD,aAAa,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;IAChD,oBAAoB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IACjD,eAAe,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAC/C,aAAa,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IAC/C,cAAc,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IAChD,iBAAiB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IACnD,iBAAiB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9C,oBAAoB,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;IACvD,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE;QAC1B,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC;QACjD,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC;QAC1C,eAAe,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;KACrD,CAAC;IAEF,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAU,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;QAChF,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QACzC,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QACxD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE;QAC5B,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,QAAQ,EAAE,cAAc,CAAU,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;KAC/F,CAAC,CAAC,QAAQ,EAAE;IAEb,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;QACtB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,iBAAiB,CAAC,CAAC;KAClE,CAAC;IAEF,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;QACzC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;KACpC,CAAC;IAEF,0EAA0E;IAC1E,4EAA4E;IAC5E,yEAAyE;IACzE,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;KAC5B,CAAC;IAEF,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;KACzC,CAAC;CACH,CAAC,CAAA;AAEF,oEAAoE;AACpE,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAA;IAC9B,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAuB;IAC1D,IAAI,MAAM,CAAC,SAAS,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC;QAAE,MAAM,IAAI,cAAc,CAAC,wCAAwC,CAAC,CAAA;IAC/J,IAAI,MAAM,CAAC,QAAQ,EAAE,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,YAAY,EAAE,CAAC;QAC7D,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YAC3G,MAAM,IAAI,cAAc,CAAC,0FAA0F,CAAC,CAAA;QACtH,CAAC;IACH,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,CAAC,qBAAqB,EAAE,oBAAoB,EAAE,2BAA2B;QACzF,uBAAuB,EAAE,eAAe,EAAE,sBAAsB,EAAE,iBAAiB;QACnF,eAAe,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB;QAC3E,sBAAsB,CAAU,EAAE,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,cAAc,CAAC,GAAG,GAAG,mBAAmB,CAAC,CAAA;IACjH,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1D,IAAI,GAAG,KAAK,WAAW,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC;YACrG,MAAM,IAAI,cAAc,CAAC,WAAW,GAAG,mBAAmB,CAAC,CAAA;QAC7D,CAAC;IACH,CAAC;IACD,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QACrC,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;YAC1G,MAAM,IAAI,cAAc,CAAC,8EAA8E,CAAC,CAAA;QAC1G,CAAC;IACH,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACrB,MAAM,IAAI,cAAc,CAAC,4EAA4E,CAAC,CAAA;QACxG,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,cAAc,CACtB,cAAc,IAAI,gBAAgB,MAAM,CAAC,SAAS,CAAC,8CAA8C,CAClG,CAAA;QACH,CAAC;QACD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzC,MAAM,IAAI,cAAc,CACtB,SAAS,IAAI,kDAAkD,MAAM,CAAC,cAAc,CAAC,sBAAsB,CAC5G,CAAA;QACH,CAAC;IACH,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,YAAY,IAAI,EAAE,EAAE,CAAC;QAChD,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,cAAc,CAAC,uBAAuB,OAAO,mCAAmC,CAAC,CAAA;QAC7F,CAAC;IACH,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,CAAC;QAChE,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,cAAc,CACtB,mCAAmC,IAAI,mCAAmC,CAC3E,CAAA;QACH,CAAC;IACH,CAAC;IACD,MAAM,aAAa,GAAG,MAAM,CAAC,KAAK,KAAK,SAAS;WAC3C,OAAO,MAAM,CAAC,KAAK,CAAC,QAAQ,KAAK,QAAQ;WACzC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QACrC,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAA;IACzB,IAAI,aAAa,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;QACvE,MAAM,IAAI,cAAc,CACtB,uDAAuD,MAAM,CAAC,cAAc,CAAC,sBAAsB,CACpG,CAAA;IACH,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,cAAc,CAAC,iCAAiC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;IAC9E,CAAC;AACH,CAAC"}
package/lib/contexts.d.ts CHANGED
@@ -23,8 +23,14 @@ export interface Activation {
23
23
  readonly peer: string;
24
24
  /** The resolved working directory this context's agent runs in. */
25
25
  readonly cwd: string;
26
- /** In-flight tasks, keyed by task id. FIFO order comes from the agent inbox. */
26
+ /** In-flight tasks, keyed by task id. The transport admits one per context. */
27
27
  readonly slots: Map<A2ATaskId, TaskSlot>;
28
+ /**
29
+ * The host briefing read at activation, merged into this context's FIRST
30
+ * visitor message and then cleared — a one-shot, so `undefined` means both
31
+ * "nothing was configured" and "already delivered".
32
+ */
33
+ preambleText?: string | undefined;
28
34
  /** Last time a request touched this context, for idle eviction. */
29
35
  lastTouchedAt: number;
30
36
  }
@@ -74,9 +80,8 @@ export declare class ContextRegistry {
74
80
  * Select Activations that may be evicted right now.
75
81
  *
76
82
  * An Activation is evictable only when nothing is owed: no in-flight task and
77
- * no recent touch. A context over the resident ceiling is evicted by least
78
- * recent touch — but a busy context is NEVER evicted to make room, because
79
- * dropping work a peer is waiting on is worse than exceeding a soft ceiling.
83
+ * no recent touch. Admission separately evicts the oldest quiet activation
84
+ * BEFORE creating a new agent. A busy context is never evicted for capacity.
80
85
  * @returns Activations the caller should dispose and remove.
81
86
  */
82
87
  evictable(): Activation[];