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
@@ -0,0 +1,168 @@
1
+ import { constants } from 'node:fs';
2
+ import { lstat, mkdir, open, opendir, realpath, rename, rm, readFile, writeFile } from 'node:fs/promises';
3
+ import { createHash, randomUUID } from 'node:crypto';
4
+ import { dirname, join, resolve, relative } from 'node:path';
5
+ import { contains } from "./isolation.js";
6
+ export async function atomicJson(file, value) {
7
+ await mkdir(dirname(file), { recursive: true, mode: 0o700 });
8
+ const temporary = `${file}.${randomUUID()}.tmp`;
9
+ await writeFile(temporary, JSON.stringify(value), { mode: 0o600 });
10
+ try {
11
+ await rename(temporary, file);
12
+ }
13
+ finally {
14
+ await rm(temporary, { force: true });
15
+ }
16
+ }
17
+ /** Never recursively remove a path outside an explicitly owned parent. */
18
+ export async function removeOwned(parent, target) {
19
+ const base = await realpath(parent), absolute = resolve(target);
20
+ if (absolute === base || !contains(base, absolute))
21
+ throw new Error('Unsafe sandbox cleanup path');
22
+ const stat = await lstat(absolute).catch(error => { if (error.code === 'ENOENT')
23
+ return undefined; throw error; });
24
+ if (!stat)
25
+ return;
26
+ if (stat.isSymbolicLink() || !contains(base, await realpath(absolute)))
27
+ throw new Error('Sandbox cleanup path was redirected');
28
+ await rm(absolute, { recursive: true, force: true });
29
+ }
30
+ /** Inter-process ownership; stale locks are reclaimed only after their owner exits. */
31
+ export class LeaseBusyError extends Error {
32
+ }
33
+ export async function acquireLease(file) {
34
+ await mkdir(dirname(file), { recursive: true, mode: 0o700 });
35
+ const token = randomUUID();
36
+ for (let attempt = 0; attempt < 2; attempt++) {
37
+ try {
38
+ const handle = await open(file, 'wx', 0o600);
39
+ try {
40
+ await handle.writeFile(JSON.stringify({ pid: process.pid, token }));
41
+ }
42
+ finally {
43
+ await handle.close();
44
+ }
45
+ return async () => {
46
+ const value = JSON.parse(await readFile(file, 'utf8').catch(() => '{}'));
47
+ if (value.token === token)
48
+ await rm(file, { force: true });
49
+ };
50
+ }
51
+ catch (error) {
52
+ if (error.code !== 'EEXIST')
53
+ throw error;
54
+ let owner;
55
+ try {
56
+ owner = JSON.parse(await readFile(file, 'utf8'));
57
+ }
58
+ catch {
59
+ throw new LeaseBusyError('Sandbox preparation is already in progress; retry shortly');
60
+ }
61
+ if (!Number.isSafeInteger(owner.pid) || owner.pid <= 0)
62
+ throw new Error('Invalid sandbox ownership lock');
63
+ try {
64
+ process.kill(owner.pid, 0);
65
+ throw new LeaseBusyError('Sandbox is already managed by another process; stop it before restarting');
66
+ }
67
+ catch (probe) {
68
+ if (probe.code !== 'ESRCH')
69
+ throw probe;
70
+ }
71
+ await rm(file, { force: true });
72
+ }
73
+ }
74
+ throw new Error('Could not acquire sandbox ownership');
75
+ }
76
+ /** Different agents may prepare the shared runtime concurrently on first launch. */
77
+ export async function waitForLease(file, timeoutMs = 1_200_000) {
78
+ const deadline = Date.now() + timeoutMs;
79
+ for (;;) {
80
+ try {
81
+ return await acquireLease(file);
82
+ }
83
+ catch (error) {
84
+ if (!(error instanceof LeaseBusyError) || Date.now() >= deadline)
85
+ throw error;
86
+ await new Promise(resolve => setTimeout(resolve, Math.min(200, Math.max(1, deadline - Date.now()))));
87
+ }
88
+ }
89
+ }
90
+ /** Copy bytes, never host links. A private projection is the only public bind. */
91
+ export async function copySafeTree(source, destination, options = {}) {
92
+ const excluded = new Set(options.excludeNames?.map(name => name.toLowerCase()));
93
+ const root = await realpath(source);
94
+ if ((await lstat(source)).isSymbolicLink())
95
+ throw new Error('Data root must not be a link');
96
+ const device = (await lstat(root)).dev;
97
+ let bytes = 0, entries = 0;
98
+ const digest = createHash('sha256');
99
+ async function verify(path) {
100
+ const stat = await lstat(path);
101
+ if (stat.isSymbolicLink() || stat.dev !== device || stat.isFile() && stat.nlink !== 1 || !contains(root, await realpath(path)))
102
+ throw new Error('Links and nested mounts are not allowed in sandbox data');
103
+ return stat;
104
+ }
105
+ async function visit(from, to) {
106
+ if (++entries > (options.maxEntries ?? 100_000))
107
+ throw new Error('Sandbox data exceeds 100000 entries');
108
+ const before = await verify(from);
109
+ digest.update(JSON.stringify([relative(root, from), before.isDirectory() ? 'dir' : before.size]));
110
+ if (before.isDirectory()) {
111
+ await mkdir(to, { recursive: true, mode: 0o700 });
112
+ const names = [];
113
+ for await (const entry of await opendir(from)) {
114
+ names.push(entry.name);
115
+ if (names.length > (options.maxEntries ?? 100_000))
116
+ throw new Error('Too many sandbox data entries');
117
+ }
118
+ for (const name of names.sort()) {
119
+ if (!excluded.has(name.toLowerCase()))
120
+ await visit(join(from, name), join(to, name));
121
+ }
122
+ }
123
+ else if (before.isFile()) {
124
+ bytes += before.size;
125
+ if (bytes > (options.maxBytes ?? 512 * 1024 * 1024))
126
+ throw new Error('Sandbox shared data exceeds 512 MiB; choose a smaller directory');
127
+ const input = await open(from, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0));
128
+ try {
129
+ const opened = await input.stat();
130
+ if (opened.ino !== before.ino || opened.dev !== before.dev || opened.nlink !== 1 || !opened.isFile())
131
+ throw new Error('Data changed during open');
132
+ await verify(from);
133
+ const output = await open(to, 'wx', 0o600);
134
+ try {
135
+ const buffer = Buffer.alloc(64 * 1024);
136
+ let offset = 0;
137
+ while (offset < before.size) {
138
+ const { bytesRead } = await input.read(buffer, 0, Math.min(buffer.length, before.size - offset), offset);
139
+ if (!bytesRead)
140
+ throw new Error('Data changed during copy');
141
+ digest.update(buffer.subarray(0, bytesRead));
142
+ await output.writeFile(buffer.subarray(0, bytesRead));
143
+ offset += bytesRead;
144
+ }
145
+ }
146
+ finally {
147
+ await output.close();
148
+ }
149
+ const after = await verify(from), final = await input.stat();
150
+ if (after.ino !== opened.ino || after.dev !== opened.dev || after.size !== before.size || after.mtimeMs !== before.mtimeMs || final.nlink !== 1)
151
+ throw new Error('Data changed during copy; retry');
152
+ }
153
+ finally {
154
+ await input.close();
155
+ }
156
+ }
157
+ else
158
+ throw new Error('Special files are not allowed in sandbox data');
159
+ const after = await verify(from);
160
+ if (after.ino !== before.ino || after.dev !== before.dev)
161
+ throw new Error('Data directory changed during copy');
162
+ }
163
+ await visit(root, destination);
164
+ if (await realpath(source) !== root)
165
+ throw new Error('Data root changed during copy');
166
+ return digest.digest('hex');
167
+ }
168
+ //# sourceMappingURL=sandbox-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-files.js","sourceRoot":"","sources":["../src/sandbox-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACzG,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY,EAAE,KAAc;IAC3D,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAC5D,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,UAAU,EAAE,MAAM,CAAA;IAC/C,MAAM,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAClE,IAAI,CAAC;QAAC,MAAM,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;IAAC,CAAC;YAAS,CAAC;QAAC,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IAAC,CAAC;AACxF,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,MAAc;IAC9D,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC/D,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAA;IAClG,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC,CAAC,MAAM,KAAK,CAAA,CAAC,CAAC,CAAC,CAAA;IACjH,IAAI,CAAC,IAAI;QAAE,OAAM;IACjB,IAAI,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;IAC9H,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AACtD,CAAC;AAED,uFAAuF;AACvF,MAAM,OAAO,cAAe,SAAQ,KAAK;CAAG;AAC5C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY;IAC7C,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;IAC5D,MAAM,KAAK,GAAG,UAAU,EAAE,CAAA;IAC1B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;QAC7C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;YAC5C,IAAI,CAAC;gBAAC,MAAM,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;YAAC,CAAC;oBAAS,CAAC;gBAAC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;YAAC,CAAC;YAC5G,OAAO,KAAK,IAAI,EAAE;gBAChB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAuB,CAAA;gBAC9F,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK;oBAAE,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YAC5D,CAAC,CAAA;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAA;YACnE,IAAI,KAAsB,CAAA;YAC1B,IAAI,CAAC;gBAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAoB,CAAA;YAAC,CAAC;YAC3E,MAAM,CAAC;gBAAC,MAAM,IAAI,cAAc,CAAC,2DAA2D,CAAC,CAAA;YAAC,CAAC;YAC/F,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,IAAI,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;YACzG,IAAI,CAAC;gBAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;gBAAC,MAAM,IAAI,cAAc,CAAC,0EAA0E,CAAC,CAAA;YAAC,CAAC;YACxI,OAAO,KAAK,EAAE,CAAC;gBAAC,IAAK,KAA+B,CAAC,IAAI,KAAK,OAAO;oBAAE,MAAM,KAAK,CAAA;YAAC,CAAC;YACpF,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;AACxD,CAAC;AAED,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY,EAAE,SAAS,GAAG,SAAS;IACpE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAA;IACvC,SAAS,CAAC;QACR,IAAI,CAAC;YAAC,OAAO,MAAM,YAAY,CAAC,IAAI,CAAC,CAAA;QAAC,CAAC;QACvC,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,CAAC,KAAK,YAAY,cAAc,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ;gBAAE,MAAM,KAAK,CAAA;YAC7E,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QACtG,CAAC;IACH,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAc,EAAE,WAAmB,EAAE,UAAwF,EAAE;IAChK,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;IAC/E,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAA;IACnC,IAAI,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAA;IAC3F,MAAM,MAAM,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAA;IACtC,IAAI,KAAK,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,CAAA;IAC1B,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAA;IACnC,KAAK,UAAU,MAAM,CAAC,IAAY;QAChC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,CAAA;QAC9B,IAAI,IAAI,CAAC,cAAc,EAAE,IAAI,IAAI,CAAC,GAAG,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAA;QAC1M,OAAO,IAAI,CAAA;IACb,CAAC;IACD,KAAK,UAAU,KAAK,CAAC,IAAY,EAAE,EAAU;QAC3C,IAAI,EAAE,OAAO,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QACvG,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAA;QACjC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjG,IAAI,MAAM,CAAC,WAAW,EAAE,EAAE,CAAC;YACzB,MAAM,KAAK,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;YACjD,MAAM,KAAK,GAAa,EAAE,CAAA;YAC1B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA;YAAC,CAAC;YAC/K,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;gBAChC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;oBAAE,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAA;YACtF,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3B,KAAK,IAAI,MAAM,CAAC,IAAI,CAAA;YACpB,IAAI,KAAK,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAA;YACvI,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,QAAQ,GAAG,CAAC,SAAS,CAAC,UAAU,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC,CAAA;YAC9G,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAA;gBACjC,IAAI,MAAM,CAAC,GAAG,KAAK,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,KAAK,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;oBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;gBACjJ,MAAM,MAAM,CAAC,IAAI,CAAC,CAAA;gBAClB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;gBAC1C,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAA;oBACtC,IAAI,MAAM,GAAG,CAAC,CAAA;oBACd,OAAO,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;wBAC5B,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;wBACxG,IAAI,CAAC,SAAS;4BAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;wBAC3D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAA;wBAC5C,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;wBAAC,MAAM,IAAI,SAAS,CAAA;oBAC5E,CAAC;gBACH,CAAC;wBAAS,CAAC;oBAAC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;gBAAC,CAAC;gBAClC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAA;gBAC5D,IAAI,KAAK,CAAC,GAAG,KAAK,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,KAAK,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,KAAK,MAAM,CAAC,OAAO,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;YACrM,CAAC;oBAAS,CAAC;gBAAC,MAAM,KAAK,CAAC,KAAK,EAAE,CAAA;YAAC,CAAC;QACnC,CAAC;;YAAM,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAA;QACvE,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAA;QAChC,IAAI,KAAK,CAAC,GAAG,KAAK,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,KAAK,MAAM,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAA;IACjH,CAAC;IACD,MAAM,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;IAC9B,IAAI,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAA;IACrF,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC7B,CAAC"}
@@ -0,0 +1,48 @@
1
+ export declare const SANDBOX_PAUSE_MS = 30000;
2
+ export declare const SANDBOX_STOP_MS = 600000;
3
+ export declare const MAX_RESIDENT_SANDBOXES = 4;
4
+ export interface SandboxVM {
5
+ pause(): Promise<void>;
6
+ resume(): Promise<void>;
7
+ stop(): Promise<void>;
8
+ }
9
+ export interface SandboxDriver<VM extends SandboxVM> {
10
+ /** A validated new public projection, committed only after the old VM stops. */
11
+ stage(peer: string): Promise<undefined | {
12
+ commit(): Promise<void>;
13
+ discard(): Promise<void>;
14
+ }>;
15
+ start(peer: string): Promise<VM>;
16
+ close?(): Promise<void>;
17
+ }
18
+ /** One lifecycle per authenticated peer, shared by all of its conversations. */
19
+ export declare class VisitorSandboxes<VM extends SandboxVM> {
20
+ private readonly driver;
21
+ private readonly options;
22
+ private readonly entries;
23
+ private capacity;
24
+ private closed;
25
+ private closing?;
26
+ private timer;
27
+ constructor(driver: SandboxDriver<VM>, options?: {
28
+ now?: () => number;
29
+ pauseMs?: number;
30
+ stopMs?: number;
31
+ maxResident?: number;
32
+ autoTick?: boolean;
33
+ warn?: (message: string) => void;
34
+ });
35
+ private now;
36
+ private locked;
37
+ private stop;
38
+ run<T>(peer: string, operation: (vm: VM) => Promise<T>, signal: AbortSignal): Promise<T>;
39
+ tick(): Promise<void>;
40
+ snapshot(): Array<{
41
+ peer: string;
42
+ state: 'running' | 'paused' | 'stopped';
43
+ active: number;
44
+ lastUsed: number;
45
+ }>;
46
+ close(): Promise<void>;
47
+ }
48
+ //# sourceMappingURL=sandbox-lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-lifecycle.d.ts","sourceRoot":"","sources":["../src/sandbox-lifecycle.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,QAAS,CAAA;AACtC,eAAO,MAAM,eAAe,SAAU,CAAA;AACtC,eAAO,MAAM,sBAAsB,IAAI,CAAA;AACvC,MAAM,WAAW,SAAS;IAAG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE;AACrG,MAAM,WAAW,aAAa,CAAC,EAAE,SAAS,SAAS;IACjD,gFAAgF;IAChF,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG;QAAE,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;QAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC,CAAA;IAC/F,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAAA;IAChC,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACxB;AAGD,gFAAgF;AAChF,qBAAa,gBAAgB,CAAC,EAAE,SAAS,SAAS;IAMpC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAqB,OAAO,CAAC,QAAQ,CAAC,OAAO;IALhF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,OAAO,CAAC,CAAe;IAC/B,OAAO,CAAC,KAAK,CAA4B;gBACZ,MAAM,EAAE,aAAa,CAAC,EAAE,CAAC,EAAmB,OAAO,GAAE;QAChF,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;KAC7H;IAON,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,MAAM;YAKA,IAAI;IAMlB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IAmClF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,QAAQ,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAG9G,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAYvB"}
@@ -0,0 +1,125 @@
1
+ export const SANDBOX_PAUSE_MS = 30_000;
2
+ export const SANDBOX_STOP_MS = 600_000;
3
+ export const MAX_RESIDENT_SANDBOXES = 4;
4
+ /** One lifecycle per authenticated peer, shared by all of its conversations. */
5
+ export class VisitorSandboxes {
6
+ driver;
7
+ options;
8
+ entries = new Map();
9
+ capacity = Promise.resolve();
10
+ closed = false;
11
+ closing;
12
+ timer;
13
+ constructor(driver, options = {}) {
14
+ this.driver = driver;
15
+ this.options = options;
16
+ if ((options.pauseMs ?? SANDBOX_PAUSE_MS) <= 0 || (options.stopMs ?? SANDBOX_STOP_MS) <= (options.pauseMs ?? SANDBOX_PAUSE_MS))
17
+ throw new Error('Invalid sandbox idle policy');
18
+ if (options.autoTick !== false) {
19
+ this.timer = setInterval(() => { void this.tick().catch(error => options.warn?.(`Sandbox idle transition failed: ${String(error)}`)); }, 1000);
20
+ this.timer.unref();
21
+ }
22
+ }
23
+ now() { return this.options.now?.() ?? Date.now(); }
24
+ locked(fn) {
25
+ const pending = this.capacity.then(fn, fn);
26
+ this.capacity = pending.catch(() => { });
27
+ return pending;
28
+ }
29
+ async stop(entry) {
30
+ if (!entry.vm)
31
+ return;
32
+ // driver.stop resumes a paused VM before graceful shutdown and keeps all disks.
33
+ await entry.vm.stop();
34
+ delete entry.vm;
35
+ entry.paused = false;
36
+ }
37
+ run(peer, operation, signal) {
38
+ if (this.closed)
39
+ return Promise.reject(new Error('Visitor sandbox manager is closed'));
40
+ if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(peer) || peer === 'owner')
41
+ return Promise.reject(new Error('Invalid visitor identity'));
42
+ let entry = this.entries.get(peer);
43
+ if (!entry) {
44
+ entry = { peer, paused: false, active: 0, lastUsed: this.now(), chain: Promise.resolve() };
45
+ this.entries.set(peer, entry);
46
+ }
47
+ const target = entry;
48
+ target.active++; // queued operations also prevent idle transitions and eviction
49
+ const execute = async () => {
50
+ signal.throwIfAborted();
51
+ if (this.closed)
52
+ throw new Error('Visitor sandbox manager is closed');
53
+ const projection = await this.driver.stage(peer);
54
+ try {
55
+ await this.locked(async () => {
56
+ signal.throwIfAborted();
57
+ if (this.closed)
58
+ throw new Error('Visitor sandbox manager is closed');
59
+ if (projection) {
60
+ await this.stop(target);
61
+ await projection.commit();
62
+ }
63
+ if (!target.vm) {
64
+ const resident = [...this.entries.values()].filter(e => e.vm);
65
+ if (resident.length >= (this.options.maxResident ?? MAX_RESIDENT_SANDBOXES)) {
66
+ const oldest = resident.filter(e => !e.active).sort((a, b) => a.lastUsed - b.lastUsed)[0];
67
+ if (!oldest)
68
+ throw new Error('Visitor tools are busy; try again later');
69
+ await this.stop(oldest);
70
+ }
71
+ target.vm = await this.driver.start(peer);
72
+ }
73
+ if (target.paused) {
74
+ await target.vm.resume();
75
+ target.paused = false;
76
+ }
77
+ });
78
+ }
79
+ finally {
80
+ await projection?.discard();
81
+ }
82
+ signal.throwIfAborted();
83
+ return await operation(target.vm);
84
+ };
85
+ const result = target.chain.then(execute, execute).finally(() => { target.active--; target.lastUsed = this.now(); });
86
+ target.chain = result.catch(() => { });
87
+ return result;
88
+ }
89
+ async tick() {
90
+ await this.locked(async () => {
91
+ if (this.closed)
92
+ return;
93
+ for (const entry of this.entries.values()) {
94
+ if (!entry.vm || entry.active)
95
+ continue;
96
+ const idle = this.now() - entry.lastUsed;
97
+ if (idle >= (this.options.stopMs ?? SANDBOX_STOP_MS))
98
+ await this.stop(entry);
99
+ else if (!entry.paused && idle >= (this.options.pauseMs ?? SANDBOX_PAUSE_MS)) {
100
+ await entry.vm.pause();
101
+ entry.paused = true;
102
+ }
103
+ }
104
+ });
105
+ }
106
+ snapshot() {
107
+ return [...this.entries.values()].map(e => ({ peer: e.peer, state: !e.vm ? 'stopped' : e.paused ? 'paused' : 'running', active: e.active, lastUsed: e.lastUsed }));
108
+ }
109
+ close() {
110
+ if (this.closing)
111
+ return this.closing;
112
+ this.closed = true;
113
+ clearInterval(this.timer);
114
+ this.closing = (async () => {
115
+ await Promise.allSettled([...this.entries.values()].map(e => e.chain));
116
+ const results = await this.locked(() => Promise.allSettled([...this.entries.values()].map(e => this.stop(e))));
117
+ await this.driver.close?.();
118
+ const failed = results.filter(r => r.status === 'rejected');
119
+ if (failed.length)
120
+ throw new AggregateError(failed.map(r => r.reason), 'Could not stop visitor sandboxes');
121
+ })();
122
+ return this.closing;
123
+ }
124
+ }
125
+ //# sourceMappingURL=sandbox-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-lifecycle.js","sourceRoot":"","sources":["../src/sandbox-lifecycle.ts"],"names":[],"mappings":"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"}
package/lib/security.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Authentication, rate limiting, inbound defanging, and outbound redaction.
3
3
  *
4
- * The real security boundary is not in this file: it is the sandbox scope and
5
- * the deterministically-rejected approval an A2A agent runs under. Injection
4
+ * The execution boundary is the rooted read_file implementation, inherited-tool
5
+ * masking and a monotonic tool guard, plus a separate owner IPC. Injection
6
6
  * defanging here is noise reduction, and must never be cited as a reason to
7
7
  * widen what a peer-driven agent is allowed to do.
8
8
  *
@@ -55,6 +55,8 @@ export declare class RateLimiter {
55
55
  /** Per-context turn counter that stops one peer from looping a context forever. */
56
56
  export declare class TurnTracker {
57
57
  private readonly turns;
58
+ get(contextId: string): number;
59
+ restore(contextId: string, count: number): void;
58
60
  /**
59
61
  * Count one inbound message on a context.
60
62
  * @param contextId - the context being extended.
@@ -1 +1 @@
1
- {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,iFAAiF;AACjF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAA;AAEjC;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAW/D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAI1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,WAAW,EAAE,WAAW,CAAC,YAAY,EAAE,MAAM,CAAC,GAC7C,YAAY,GAAG,SAAS,CAO1B;AAED,uEAAuE;AACvE,qBAAa,WAAW;IAQpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG;IARtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoC;IAEzD;;;OAGG;gBAEgB,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,MAAM,MAAiB;IAG/C;;;;OAIG;IACH,KAAK,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO;IAYtC,2CAA2C;IAC3C,KAAK,IAAI,IAAI;CAGd;AAED,mFAAmF;AACnF,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;IAElD;;;;OAIG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAMhC;;;OAGG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI9B,2CAA2C;IAC3C,KAAK,IAAI,IAAI;CAGd;AAkBD,+CAA+C;AAC/C,eAAO,MAAM,QAAQ,eAAe,CAAA;AAEpC;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlD;AAsBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAInD"}
1
+ {"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,iFAAiF;AACjF,MAAM,MAAM,YAAY,GAAG,MAAM,CAAA;AAEjC;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAW/D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAI1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,WAAW,EAAE,WAAW,CAAC,YAAY,EAAE,MAAM,CAAC,GAC7C,YAAY,GAAG,SAAS,CAO1B;AAED,uEAAuE;AACvE,qBAAa,WAAW;IAQpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG;IARtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoC;IAEzD;;;OAGG;gBAEgB,SAAS,EAAE,MAAM,EACjB,GAAG,GAAE,MAAM,MAAiB;IAG/C;;;;OAIG;IACH,KAAK,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO;IAYtC,2CAA2C;IAC3C,KAAK,IAAI,IAAI;CAGd;AAED,mFAAmF;AACnF,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA4B;IAElD,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAC9B,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAE/C;;;;OAIG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAMhC;;;OAGG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI9B,2CAA2C;IAC3C,KAAK,IAAI,IAAI;CAGd;AAkBD,+CAA+C;AAC/C,eAAO,MAAM,QAAQ,eAAe,CAAA;AAEpC;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlD;AAsBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAInD"}
package/lib/security.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Authentication, rate limiting, inbound defanging, and outbound redaction.
3
3
  *
4
- * The real security boundary is not in this file: it is the sandbox scope and
5
- * the deterministically-rejected approval an A2A agent runs under. Injection
4
+ * The execution boundary is the rooted read_file implementation, inherited-tool
5
+ * masking and a monotonic tool guard, plus a separate owner IPC. Injection
6
6
  * defanging here is noise reduction, and must never be cited as a reason to
7
7
  * widen what a peer-driven agent is allowed to do.
8
8
  *
@@ -94,6 +94,8 @@ export class RateLimiter {
94
94
  /** Per-context turn counter that stops one peer from looping a context forever. */
95
95
  export class TurnTracker {
96
96
  turns = new Map();
97
+ get(contextId) { return this.turns.get(contextId) ?? 0; }
98
+ restore(contextId, count) { this.turns.set(contextId, count); }
97
99
  /**
98
100
  * Count one inbound message on a context.
99
101
  * @param contextId - the context being extended.
@@ -1 +1 @@
1
- {"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"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"}
package/lib/state.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import type { SessionEvent } from '@deepseek-ai/dsh-session';
2
+ import type { A2ATask } from './protocol/index.ts';
3
+ import type { A2AServerConfig } from './config.ts';
4
+ export declare function deploymentKey(config: Pick<A2AServerConfig, 'basePath' | 'isolation' | 'admin'>): string;
5
+ export interface ContextCheckpoint {
6
+ id: string;
7
+ peer: string;
8
+ cwd: string;
9
+ updatedAt: number;
10
+ turns: number;
11
+ /** Last fully idle event prefix; accepted-but-unfinished work is never replayed. */
12
+ events: SessionEvent[];
13
+ tasks: A2ATask[];
14
+ inputs: Record<string, string>;
15
+ exhausted?: boolean;
16
+ }
17
+ /** Checkpoints are written before acceptance and after idle; directories must not be shared by processes. */
18
+ export declare class StateStore {
19
+ private readonly config;
20
+ readonly directory: string;
21
+ private records;
22
+ private readonly lock;
23
+ private budgets;
24
+ constructor(config: A2AServerConfig);
25
+ private write;
26
+ save(record: ContextCheckpoint): void;
27
+ /** Fail-closed live outcome when disk I/O itself is unavailable. */
28
+ rememberFailure(record: ContextCheckpoint): void;
29
+ get(id: string, peer: string): ContextCheckpoint | undefined;
30
+ values(): ContextCheckpoint[];
31
+ ensureRoom(protectedIds?: ReadonlySet<string>): void;
32
+ prune(protectedIds?: ReadonlySet<string>): void;
33
+ assertQuota(peer: string): void;
34
+ charge(peer: string): void;
35
+ close(): void;
36
+ }
37
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,wBAAgB,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC,GAAG,MAAM,CAEvG;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,MAAM,EAAE,YAAY,EAAE,CAAA;IACtB,KAAK,EAAE,OAAO,EAAE,CAAA;IAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAYD,6GAA6G;AAC7G,qBAAa,UAAU;IAMT,OAAO,CAAC,QAAQ,CAAC,MAAM;IALnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAQ;IAC7B,OAAO,CAAC,OAAO,CAAqD;gBAEvC,MAAM,EAAE,eAAe;IA0CpD,OAAO,CAAC,KAAK;IASb,IAAI,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAKrC,oEAAoE;IACpE,eAAe,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAEhD,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAK5D,MAAM,IAAI,iBAAiB,EAAE;IAE7B,UAAU,CAAC,YAAY,GAAE,WAAW,CAAC,MAAM,CAAa,GAAG,IAAI;IAK/D,KAAK,CAAC,YAAY,GAAE,WAAW,CAAC,MAAM,CAAa,GAAG,IAAI;IAS1D,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAO/B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAU1B,KAAK,IAAI,IAAI;CACd"}
package/lib/state.js ADDED
@@ -0,0 +1,146 @@
1
+ /** Bounded, private checkpoints for task retention and idle context recovery. */
2
+ import { createHash, randomUUID } from 'node:crypto';
3
+ import { mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync, openSync, closeSync, statSync } from 'node:fs';
4
+ import { homedir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { z } from 'zod';
7
+ export function deploymentKey(config) {
8
+ return createHash('sha256').update(`${config.isolation.workspaceRoot}\n${config.basePath}\n${config.admin?.tokenEnv ?? ''}`).digest('hex').slice(0, 24);
9
+ }
10
+ const checkpointSchema = z.object({
11
+ id: z.string().uuid(), peer: z.string(), cwd: z.string(), updatedAt: z.number(),
12
+ turns: z.number().int().nonnegative(), events: z.array(z.unknown()),
13
+ inputs: z.record(z.string(), z.string()).default({}), exhausted: z.boolean().optional(),
14
+ tasks: z.array(z.object({ id: z.string(), contextId: z.string(), status: z.object({
15
+ state: z.string(), timestamp: z.string(),
16
+ }).passthrough() }).passthrough()),
17
+ });
18
+ const terminal = new Set(['TASK_STATE_COMPLETED', 'TASK_STATE_FAILED', 'TASK_STATE_CANCELED', 'TASK_STATE_REJECTED']);
19
+ /** Checkpoints are written before acceptance and after idle; directories must not be shared by processes. */
20
+ export class StateStore {
21
+ config;
22
+ directory;
23
+ records = new Map();
24
+ lock;
25
+ budgets = {};
26
+ constructor(config) {
27
+ this.config = config;
28
+ this.directory = config.storage.directory ?? join(homedir(), '.dsh-a2a', deploymentKey(config));
29
+ mkdirSync(this.directory, { recursive: true, mode: 0o700 });
30
+ this.lock = join(this.directory, 'process.lock');
31
+ try {
32
+ const pid = Number(readFileSync(this.lock, 'utf8'));
33
+ if (!Number.isSafeInteger(pid) || pid <= 0)
34
+ throw new Error('A2A state lock is incomplete or invalid');
35
+ let live = true;
36
+ try {
37
+ process.kill(pid, 0);
38
+ }
39
+ catch (error) {
40
+ live = error.code !== 'ESRCH';
41
+ }
42
+ if (live)
43
+ throw new Error('A2A state directory is already in use');
44
+ rmSync(this.lock);
45
+ }
46
+ catch (error) {
47
+ if (error.code !== 'ENOENT')
48
+ throw error;
49
+ }
50
+ const fd = openSync(this.lock, 'wx', 0o600);
51
+ writeFileSync(fd, String(process.pid));
52
+ closeSync(fd);
53
+ try {
54
+ for (const file of readdirSync(this.directory)) {
55
+ if (!/^[0-9a-f-]{36}\.json$/.test(file))
56
+ continue;
57
+ if (statSync(join(this.directory, file)).size > config.storage.maxContextBytes)
58
+ throw new Error('stored A2A checkpoint exceeds configured size');
59
+ if (this.records.size >= config.storage.maxContexts)
60
+ throw new Error('stored A2A contexts exceed configured capacity');
61
+ const parsed = checkpointSchema.parse(JSON.parse(readFileSync(join(this.directory, file), 'utf8')));
62
+ if (`${parsed.id}.json` !== file)
63
+ throw new Error('A2A checkpoint id mismatch');
64
+ // The harness validates the event vocabulary and sequence again at restore.
65
+ const record = parsed;
66
+ for (const task of record.tasks) {
67
+ if (task.contextId !== record.id)
68
+ throw new Error('A2A checkpoint task ownership mismatch');
69
+ if (!terminal.has(task.status.state)) {
70
+ task.status = { state: 'TASK_STATE_FAILED', timestamp: new Date().toISOString() };
71
+ task.metadata = { dsh: { stopReason: 'process_restart' } };
72
+ }
73
+ }
74
+ this.records.set(record.id, record);
75
+ }
76
+ try {
77
+ this.budgets = z.record(z.string(), z.object({ day: z.string(), count: z.number().int().nonnegative() }))
78
+ .parse(JSON.parse(readFileSync(join(this.directory, 'budgets.json'), 'utf8')));
79
+ }
80
+ catch (error) {
81
+ if (error.code !== 'ENOENT')
82
+ throw error;
83
+ }
84
+ this.prune();
85
+ }
86
+ catch (error) {
87
+ this.close();
88
+ throw error;
89
+ }
90
+ }
91
+ write(name, value) {
92
+ const text = JSON.stringify(value);
93
+ if (Buffer.byteLength(text) > this.config.storage.maxContextBytes)
94
+ throw new Error('A2A checkpoint size limit exceeded');
95
+ const target = join(this.directory, name);
96
+ const temporary = `${target}.${randomUUID()}.tmp`;
97
+ try {
98
+ writeFileSync(temporary, text, { mode: 0o600 });
99
+ renameSync(temporary, target);
100
+ }
101
+ finally {
102
+ rmSync(temporary, { force: true });
103
+ }
104
+ }
105
+ save(record) {
106
+ this.write(`${record.id}.json`, record);
107
+ this.records.set(record.id, record);
108
+ }
109
+ /** Fail-closed live outcome when disk I/O itself is unavailable. */
110
+ rememberFailure(record) { this.records.set(record.id, record); }
111
+ get(id, peer) {
112
+ const record = this.records.get(id);
113
+ return record?.peer === peer ? record : undefined;
114
+ }
115
+ values() { return [...this.records.values()]; }
116
+ ensureRoom(protectedIds = new Set()) {
117
+ this.prune(protectedIds);
118
+ if (this.records.size >= this.config.storage.maxContexts)
119
+ throw new Error('A2A retained context capacity reached');
120
+ }
121
+ prune(protectedIds = new Set()) {
122
+ for (const [id, record] of this.records) {
123
+ if (!protectedIds.has(id) && record.updatedAt < Date.now() - this.config.storage.retentionMs) {
124
+ rmSync(join(this.directory, `${id}.json`), { force: true });
125
+ this.records.delete(id);
126
+ }
127
+ }
128
+ }
129
+ assertQuota(peer) {
130
+ const current = this.budgets[peer];
131
+ if (current?.day === new Date().toISOString().slice(0, 10) && current.count >= this.config.maxTasksPerPeerPerDay) {
132
+ throw new Error('daily task quota exceeded');
133
+ }
134
+ }
135
+ charge(peer) {
136
+ const day = new Date().toISOString().slice(0, 10);
137
+ const previous = this.budgets[peer];
138
+ const count = previous?.day === day ? previous.count : 0;
139
+ this.assertQuota(peer);
140
+ const next = { ...this.budgets, [peer]: { day, count: count + 1 } };
141
+ this.write('budgets.json', next);
142
+ this.budgets = next;
143
+ }
144
+ close() { rmSync(this.lock, { force: true }); }
145
+ }
146
+ //# sourceMappingURL=state.js.map