@the-open-engine/zeroshot 6.22.0 → 6.24.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 (73) hide show
  1. package/docker/zeroshot-cluster/Dockerfile +7 -0
  2. package/lib/agent-cli-provider/adapters/omp.d.ts +3 -1
  3. package/lib/agent-cli-provider/adapters/omp.d.ts.map +1 -1
  4. package/lib/agent-cli-provider/adapters/omp.js +252 -269
  5. package/lib/agent-cli-provider/adapters/omp.js.map +1 -1
  6. package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -1
  7. package/lib/agent-cli-provider/contract-invoke.js +68 -14
  8. package/lib/agent-cli-provider/contract-invoke.js.map +1 -1
  9. package/lib/agent-cli-provider/contract-options.d.ts.map +1 -1
  10. package/lib/agent-cli-provider/contract-options.js +5 -3
  11. package/lib/agent-cli-provider/contract-options.js.map +1 -1
  12. package/lib/agent-cli-provider/index.d.ts +6 -0
  13. package/lib/agent-cli-provider/index.d.ts.map +1 -1
  14. package/lib/agent-cli-provider/index.js +12 -1
  15. package/lib/agent-cli-provider/index.js.map +1 -1
  16. package/lib/agent-cli-provider/omp-release.d.ts +6 -1
  17. package/lib/agent-cli-provider/omp-release.d.ts.map +1 -1
  18. package/lib/agent-cli-provider/omp-release.js +41 -2
  19. package/lib/agent-cli-provider/omp-release.js.map +1 -1
  20. package/lib/agent-cli-provider/omp-rpc-bounds.d.ts +7 -0
  21. package/lib/agent-cli-provider/omp-rpc-bounds.d.ts.map +1 -0
  22. package/lib/agent-cli-provider/omp-rpc-bounds.js +27 -0
  23. package/lib/agent-cli-provider/omp-rpc-bounds.js.map +1 -0
  24. package/lib/agent-cli-provider/omp-rpc-driver.d.ts +40 -0
  25. package/lib/agent-cli-provider/omp-rpc-driver.d.ts.map +1 -0
  26. package/lib/agent-cli-provider/omp-rpc-driver.js +494 -0
  27. package/lib/agent-cli-provider/omp-rpc-driver.js.map +1 -0
  28. package/lib/agent-cli-provider/omp-rpc-events.d.ts +28 -0
  29. package/lib/agent-cli-provider/omp-rpc-events.d.ts.map +1 -0
  30. package/lib/agent-cli-provider/omp-rpc-events.js +264 -0
  31. package/lib/agent-cli-provider/omp-rpc-events.js.map +1 -0
  32. package/lib/agent-cli-provider/omp-rpc-protocol.d.ts +1 -1
  33. package/lib/agent-cli-provider/omp-rpc-protocol.d.ts.map +1 -1
  34. package/lib/agent-cli-provider/omp-rpc-protocol.js +8 -3
  35. package/lib/agent-cli-provider/omp-rpc-protocol.js.map +1 -1
  36. package/lib/agent-cli-provider/omp-rpc-session.d.ts +17 -0
  37. package/lib/agent-cli-provider/omp-rpc-session.d.ts.map +1 -0
  38. package/lib/agent-cli-provider/omp-rpc-session.js +6 -0
  39. package/lib/agent-cli-provider/omp-rpc-session.js.map +1 -0
  40. package/lib/agent-cli-provider/provider-registry.d.ts +33 -17
  41. package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
  42. package/lib/agent-cli-provider/provider-registry.js +63 -11
  43. package/lib/agent-cli-provider/provider-registry.js.map +1 -1
  44. package/lib/agent-cli-provider/types.d.ts +13 -11
  45. package/lib/agent-cli-provider/types.d.ts.map +1 -1
  46. package/lib/agent-cli-provider/types.js.map +1 -1
  47. package/lib/docker-config.js +122 -5
  48. package/package.json +2 -1
  49. package/src/agent/agent-lifecycle.js +12 -1
  50. package/src/agent/provider-session.js +1 -0
  51. package/src/agent-cli-provider/adapters/omp.ts +276 -329
  52. package/src/agent-cli-provider/contract-invoke.ts +86 -15
  53. package/src/agent-cli-provider/contract-options.ts +5 -3
  54. package/src/agent-cli-provider/index.ts +13 -0
  55. package/src/agent-cli-provider/omp-release.ts +50 -1
  56. package/src/agent-cli-provider/omp-rpc-bounds.ts +28 -0
  57. package/src/agent-cli-provider/omp-rpc-driver.ts +611 -0
  58. package/src/agent-cli-provider/omp-rpc-events.ts +318 -0
  59. package/src/agent-cli-provider/omp-rpc-protocol.ts +8 -3
  60. package/src/agent-cli-provider/omp-rpc-session.ts +20 -0
  61. package/src/agent-cli-provider/provider-registry.ts +118 -23
  62. package/src/agent-cli-provider/types.ts +14 -11
  63. package/src/command-cleanup-ownership.js +307 -0
  64. package/src/isolation-manager.js +535 -89
  65. package/src/omp-config-overlay.js +96 -0
  66. package/src/orchestrator.js +31 -6
  67. package/src/preflight.js +50 -5
  68. package/src/watcher-prompt-channel.js +209 -0
  69. package/task-lib/command-spec-cleanup.js +1 -262
  70. package/task-lib/provider-helper-runtime.js +6 -0
  71. package/task-lib/rpc-watcher.js +186 -0
  72. package/task-lib/runner.js +40 -6
  73. package/task-lib/watcher-output-runtime.js +32 -0
@@ -0,0 +1,307 @@
1
+ const { lstat, realpath, rm, unlink } = require('fs/promises');
2
+ const { lstatSync, realpathSync, rmSync, unlinkSync } = require('fs');
3
+ const { tmpdir } = require('os');
4
+ const { basename, dirname, isAbsolute, resolve } = require('path');
5
+
6
+ const {
7
+ isCanonicalClaudeSettingsOverlayDirectory,
8
+ isClaudeSettingsOverlayDirectory,
9
+ } = require('./worktree-claude-config');
10
+ const { isCanonicalOmpConfigOverlayDirectory } = require('./omp-config-overlay');
11
+
12
+ const CLEANUP_METADATA_KEYS = ['kind', 'path', 'provider', 'reason'];
13
+ const SCHEMA_DIRECTORY_PATTERN = /^zeroshot-schema-[A-Za-z0-9_-]+$/u;
14
+ const SCHEMA_FILE_PATTERN =
15
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\.json$/u;
16
+ const POLICY_DIRECTORY_PATTERN = /^zeroshot-gemini-policy-[A-Za-z0-9_-]+$/u;
17
+ const POLICY_FILE_PATTERN =
18
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\.toml$/u;
19
+ const OPENCODE_CONFIG_DIRECTORY_PATTERN = /^zeroshot-opencode-config-[A-Za-z0-9_-]+$/u;
20
+
21
+ function assertClosedCleanupMetadata(cleanupPath, metadata) {
22
+ if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) {
23
+ throw new Error(`Refusing cleanup without closed ownership metadata: ${cleanupPath}`);
24
+ }
25
+ const keys = Object.keys(metadata).sort();
26
+ if (
27
+ keys.length !== CLEANUP_METADATA_KEYS.length ||
28
+ keys.some((key, index) => key !== CLEANUP_METADATA_KEYS[index])
29
+ ) {
30
+ throw new Error(`Refusing cleanup with open ownership metadata: ${cleanupPath}`);
31
+ }
32
+ if (metadata.path !== cleanupPath) {
33
+ throw new Error(`Refusing cleanup with mismatched ownership path: ${cleanupPath}`);
34
+ }
35
+ }
36
+
37
+ function createCleanupPlan(commandSpec) {
38
+ if (!Array.isArray(commandSpec?.cleanup) || !Array.isArray(commandSpec?.cleanupMetadata)) {
39
+ throw new Error('Refusing cleanup with malformed cleanup collections');
40
+ }
41
+ if (commandSpec.cleanup.length !== commandSpec.cleanupMetadata.length) {
42
+ throw new Error('Refusing cleanup without one closed metadata receipt per path');
43
+ }
44
+ const uniquePaths = new Set(commandSpec.cleanup);
45
+ if (uniquePaths.size !== commandSpec.cleanup.length) {
46
+ throw new Error('Refusing cleanup with duplicate cleanup paths');
47
+ }
48
+
49
+ return commandSpec.cleanup.map((cleanupPath) => {
50
+ if (typeof cleanupPath !== 'string' || cleanupPath.length === 0) {
51
+ throw new Error('Refusing cleanup with a non-string or empty path');
52
+ }
53
+ const matches = commandSpec.cleanupMetadata.filter(
54
+ (metadata) => metadata?.path === cleanupPath
55
+ );
56
+ if (matches.length !== 1) {
57
+ throw new Error(`Refusing cleanup without exact ownership metadata: ${cleanupPath}`);
58
+ }
59
+ assertClosedCleanupMetadata(cleanupPath, matches[0]);
60
+ return { cleanupPath, metadata: matches[0] };
61
+ });
62
+ }
63
+
64
+ function isCanonicalNamedTempDirectory(cleanupPath, pattern) {
65
+ return (
66
+ isAbsolute(cleanupPath) &&
67
+ resolve(cleanupPath) === cleanupPath &&
68
+ dirname(cleanupPath) === resolve(tmpdir()) &&
69
+ pattern.test(basename(cleanupPath))
70
+ );
71
+ }
72
+
73
+ function assertOwnedNamedTempDirectory(cleanupPath, pattern) {
74
+ if (!isCanonicalNamedTempDirectory(cleanupPath, pattern)) {
75
+ throw new Error(`Refusing unowned temporary directory cleanup: ${cleanupPath}`);
76
+ }
77
+ let stat;
78
+ try {
79
+ stat = lstatSync(cleanupPath);
80
+ } catch (error) {
81
+ if (error?.code === 'ENOENT') return true;
82
+ throw error;
83
+ }
84
+ const tempRoot = realpathSync(tmpdir());
85
+ const realDirectory = realpathSync(cleanupPath);
86
+ if (
87
+ stat.isSymbolicLink() ||
88
+ !stat.isDirectory() ||
89
+ dirname(realDirectory) !== tempRoot ||
90
+ basename(realDirectory) !== basename(cleanupPath)
91
+ ) {
92
+ throw new Error(`Refusing unowned temporary directory cleanup: ${cleanupPath}`);
93
+ }
94
+ return false;
95
+ }
96
+
97
+ function assertOwnedOmpConfigDirectory(cleanupPath) {
98
+ if (!isCanonicalOmpConfigOverlayDirectory(cleanupPath)) {
99
+ throw new Error(`Refusing unowned temporary directory cleanup: ${cleanupPath}`);
100
+ }
101
+ let stat;
102
+ try {
103
+ stat = lstatSync(cleanupPath);
104
+ } catch (error) {
105
+ if (error?.code === 'ENOENT') return true;
106
+ throw error;
107
+ }
108
+ const tempRoot = realpathSync(tmpdir());
109
+ const realDirectory = realpathSync(cleanupPath);
110
+ const ownedByProcess = typeof process.getuid !== 'function' || stat.uid === process.getuid();
111
+ const privateMode = process.platform === 'win32' || (stat.mode & 0o777) === 0o700;
112
+ if (
113
+ stat.isSymbolicLink() ||
114
+ !stat.isDirectory() ||
115
+ !ownedByProcess ||
116
+ !privateMode ||
117
+ dirname(realDirectory) !== tempRoot ||
118
+ basename(realDirectory) !== basename(cleanupPath)
119
+ ) {
120
+ throw new Error(`Refusing unowned temporary directory cleanup: ${cleanupPath}`);
121
+ }
122
+ return false;
123
+ }
124
+
125
+ function assertOwnedTempDirectory(cleanupPath, metadata) {
126
+ const isOpenCodeConfig =
127
+ metadata.provider === 'opencode' && metadata.reason === 'isolated-config';
128
+ if (isOpenCodeConfig) {
129
+ return assertOwnedNamedTempDirectory(cleanupPath, OPENCODE_CONFIG_DIRECTORY_PATTERN);
130
+ }
131
+
132
+ const isOmpConfig = metadata.provider === 'omp' && metadata.reason === 'isolated-config';
133
+ if (isOmpConfig) {
134
+ return assertOwnedOmpConfigDirectory(cleanupPath);
135
+ }
136
+
137
+ if (
138
+ metadata.provider !== 'claude' ||
139
+ metadata.reason !== 'settings-overlay' ||
140
+ !isCanonicalClaudeSettingsOverlayDirectory(cleanupPath)
141
+ ) {
142
+ throw new Error(`Refusing unowned temporary directory cleanup: ${cleanupPath}`);
143
+ }
144
+ if (isClaudeSettingsOverlayDirectory(cleanupPath)) {
145
+ return false;
146
+ }
147
+ try {
148
+ lstatSync(cleanupPath);
149
+ } catch (error) {
150
+ if (error?.code === 'ENOENT') return true;
151
+ throw error;
152
+ }
153
+ throw new Error(`Refusing unowned temporary directory cleanup: ${cleanupPath}`);
154
+ }
155
+
156
+ function assertCanonicalSchemaPath(cleanupPath, metadata) {
157
+ const isCodexSchema =
158
+ metadata.kind === 'temp-file' &&
159
+ metadata.provider === 'codex' &&
160
+ metadata.reason === 'output-schema';
161
+ const isGeminiPolicy =
162
+ metadata.kind === 'temp-file' &&
163
+ metadata.provider === 'gemini' &&
164
+ metadata.reason === 'admin-policy';
165
+ if (
166
+ (!isCodexSchema && !isGeminiPolicy) ||
167
+ !isAbsolute(cleanupPath) ||
168
+ resolve(cleanupPath) !== cleanupPath
169
+ ) {
170
+ throw new Error(
171
+ `Refusing unowned ${isGeminiPolicy ? 'admin-policy' : 'output-schema'} cleanup: ${cleanupPath}`
172
+ );
173
+ }
174
+ const ownedDirectory = dirname(cleanupPath);
175
+ const matchesOwnedName = isCodexSchema
176
+ ? SCHEMA_DIRECTORY_PATTERN.test(basename(ownedDirectory)) &&
177
+ SCHEMA_FILE_PATTERN.test(basename(cleanupPath))
178
+ : POLICY_DIRECTORY_PATTERN.test(basename(ownedDirectory)) &&
179
+ POLICY_FILE_PATTERN.test(basename(cleanupPath));
180
+ if (dirname(ownedDirectory) !== resolve(tmpdir()) || !matchesOwnedName) {
181
+ throw new Error(
182
+ `Refusing non-canonical ${isGeminiPolicy ? 'admin-policy' : 'output-schema'} cleanup: ${cleanupPath}`
183
+ );
184
+ }
185
+ }
186
+
187
+ async function assertOwnedSchemaFile(cleanupPath, metadata) {
188
+ assertCanonicalSchemaPath(cleanupPath, metadata);
189
+ const schemaDirectory = dirname(cleanupPath);
190
+ const [tempRoot, realSchemaDirectory, directoryStat, schemaStat] = await Promise.all([
191
+ realpath(tmpdir()),
192
+ realpath(schemaDirectory),
193
+ lstat(schemaDirectory),
194
+ lstat(cleanupPath),
195
+ ]);
196
+ if (
197
+ directoryStat.isSymbolicLink() ||
198
+ !directoryStat.isDirectory() ||
199
+ dirname(realSchemaDirectory) !== tempRoot ||
200
+ basename(realSchemaDirectory) !== basename(schemaDirectory) ||
201
+ schemaStat.isSymbolicLink() ||
202
+ !schemaStat.isFile()
203
+ ) {
204
+ throw new Error(`Refusing non-regular or escaped output-schema cleanup: ${cleanupPath}`);
205
+ }
206
+ }
207
+
208
+ function assertOwnedSchemaFileSync(cleanupPath, metadata) {
209
+ assertCanonicalSchemaPath(cleanupPath, metadata);
210
+ const schemaDirectory = dirname(cleanupPath);
211
+ const tempRoot = realpathSync(tmpdir());
212
+ const realSchemaDirectory = realpathSync(schemaDirectory);
213
+ const directoryStat = lstatSync(schemaDirectory);
214
+ const schemaStat = lstatSync(cleanupPath);
215
+ if (
216
+ directoryStat.isSymbolicLink() ||
217
+ !directoryStat.isDirectory() ||
218
+ dirname(realSchemaDirectory) !== tempRoot ||
219
+ basename(realSchemaDirectory) !== basename(schemaDirectory) ||
220
+ schemaStat.isSymbolicLink() ||
221
+ !schemaStat.isFile()
222
+ ) {
223
+ throw new Error(`Refusing non-regular or escaped output-schema cleanup: ${cleanupPath}`);
224
+ }
225
+ }
226
+
227
+ async function removeCleanupPath(cleanupPath, metadata) {
228
+ if (metadata.kind === 'temp-directory') {
229
+ if (assertOwnedTempDirectory(cleanupPath, metadata)) return;
230
+ await rm(cleanupPath, { recursive: true, force: true });
231
+ return;
232
+ }
233
+ await assertOwnedSchemaFile(cleanupPath, metadata);
234
+ await unlink(cleanupPath);
235
+ }
236
+
237
+ function removeCleanupPathSync(cleanupPath, metadata) {
238
+ if (metadata.kind === 'temp-directory') {
239
+ if (assertOwnedTempDirectory(cleanupPath, metadata)) return;
240
+ rmSync(cleanupPath, { recursive: true, force: true });
241
+ return;
242
+ }
243
+ assertOwnedSchemaFileSync(cleanupPath, metadata);
244
+ unlinkSync(cleanupPath);
245
+ }
246
+
247
+ /**
248
+ * Build one idempotent cleanup owner for a provider command. Callers may run it
249
+ * only after the persisted provider termination boundary is terminal.
250
+ */
251
+ function createCommandSpecCleanup(commandSpec, logFailure) {
252
+ let started = false;
253
+ let result = true;
254
+
255
+ return {
256
+ async run() {
257
+ if (started) return result;
258
+ started = true;
259
+ let cleanupPlan;
260
+ try {
261
+ cleanupPlan = createCleanupPlan(commandSpec);
262
+ } catch (error) {
263
+ logFailure('<command-cleanup>', error);
264
+ result = false;
265
+ return result;
266
+ }
267
+ let succeeded = true;
268
+ for (const { cleanupPath, metadata } of cleanupPlan) {
269
+ try {
270
+ await removeCleanupPath(cleanupPath, metadata);
271
+ } catch (error) {
272
+ if (error?.code === 'ENOENT') continue;
273
+ succeeded = false;
274
+ logFailure(cleanupPath, error);
275
+ }
276
+ }
277
+ result = succeeded;
278
+ return result;
279
+ },
280
+ runSync() {
281
+ if (started) return result;
282
+ started = true;
283
+ let cleanupPlan;
284
+ try {
285
+ cleanupPlan = createCleanupPlan(commandSpec);
286
+ } catch (error) {
287
+ logFailure('<command-cleanup>', error);
288
+ result = false;
289
+ return result;
290
+ }
291
+ let succeeded = true;
292
+ for (const { cleanupPath, metadata } of cleanupPlan) {
293
+ try {
294
+ removeCleanupPathSync(cleanupPath, metadata);
295
+ } catch (error) {
296
+ if (error?.code === 'ENOENT') continue;
297
+ succeeded = false;
298
+ logFailure(cleanupPath, error);
299
+ }
300
+ }
301
+ result = succeeded;
302
+ return result;
303
+ },
304
+ };
305
+ }
306
+
307
+ module.exports = { createCommandSpecCleanup };