@the-open-engine/zeroshot 6.7.1 → 6.8.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.
- package/cli/index.js +114 -16
- package/lib/agent-cli-provider/adapters/common.js +1 -1
- package/lib/agent-cli-provider/adapters/common.js.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode-models.d.ts +7 -0
- package/lib/agent-cli-provider/adapters/opencode-models.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode-models.js +87 -0
- package/lib/agent-cli-provider/adapters/opencode-models.js.map +1 -0
- package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode.js +6 -58
- package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js +27 -13
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
- package/lib/agent-cli-provider/types.d.ts +1 -0
- package/lib/agent-cli-provider/types.d.ts.map +1 -1
- package/lib/agent-cli-provider/types.js.map +1 -1
- package/lib/git-remote-utils.js +90 -7
- package/package.json +6 -15
- package/protocol/openengine-cluster/v1/worker.schema.json +47 -0
- package/scripts/assert-release-published.js +161 -2
- package/scripts/release-dry-run.js +83 -0
- package/scripts/release-preflight.js +24 -12
- package/scripts/release-recovery.js +149 -0
- package/scripts/semantic-release-notes.js +44 -0
- package/scripts/setup-merge-queue.sh +104 -118
- package/src/agent/agent-task-executor.js +18 -9
- package/src/agent-cli-provider/adapters/common.ts +1 -1
- package/src/agent-cli-provider/adapters/opencode-models.ts +100 -0
- package/src/agent-cli-provider/adapters/opencode.ts +8 -65
- package/src/agent-cli-provider/single-agent-runtime.ts +51 -33
- package/src/agent-cli-provider/types.ts +1 -0
- package/src/agent-wrapper.js +14 -2
- package/src/agents/git-pusher-template.js +94 -19
- package/src/attach/socket-discovery.js +9 -19
- package/src/attach/socket-paths.js +85 -0
- package/src/claude-task-runner.js +121 -37
- package/src/config-validator.js +2 -2
- package/src/isolation-manager.js +164 -74
- package/src/orchestrator.js +83 -34
- package/src/providers/index.js +5 -0
- package/src/task-run-model-args.js +155 -0
- package/task-lib/attachable-watcher.js +3 -9
- package/task-lib/commands/run.js +17 -2
- package/task-lib/runner.js +87 -29
|
@@ -11,14 +11,9 @@ import {
|
|
|
11
11
|
type BuildProviderCommandOptions,
|
|
12
12
|
type CommandSpec,
|
|
13
13
|
type ErrorClassification,
|
|
14
|
-
type LevelModelSpec,
|
|
15
|
-
type LevelOverrides,
|
|
16
|
-
type ModelCatalogEntry,
|
|
17
|
-
type ModelLevel,
|
|
18
14
|
type OpencodeCliFeatures,
|
|
19
15
|
type OutputEvent,
|
|
20
16
|
type ProviderAdapter,
|
|
21
|
-
type ResolvedModelSpec,
|
|
22
17
|
type WarningMetadata,
|
|
23
18
|
} from '../types';
|
|
24
19
|
import {
|
|
@@ -26,55 +21,16 @@ import {
|
|
|
26
21
|
commandSpec,
|
|
27
22
|
createParserState,
|
|
28
23
|
optionFeatures,
|
|
29
|
-
resolveModelSpecWithConfig,
|
|
30
24
|
unsupportedSessionControlWarnings,
|
|
31
|
-
validateModelIdFromCatalog,
|
|
32
25
|
warning,
|
|
33
26
|
} from './common';
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
'google/gemini-1.5-flash': { rank: 1 },
|
|
42
|
-
'google/gemini-1.5-flash-8b': { rank: 1 },
|
|
43
|
-
'google/gemini-1.5-pro': { rank: 1 },
|
|
44
|
-
'google/gemini-2.0-flash': { rank: 1 },
|
|
45
|
-
'google/gemini-2.0-flash-lite': { rank: 1 },
|
|
46
|
-
'google/gemini-2.5-flash': { rank: 1 },
|
|
47
|
-
'google/gemini-2.5-flash-image': { rank: 1 },
|
|
48
|
-
'google/gemini-2.5-flash-image-preview': { rank: 1 },
|
|
49
|
-
'google/gemini-2.5-flash-lite': { rank: 1 },
|
|
50
|
-
'google/gemini-2.5-flash-lite-preview-06-17': { rank: 1 },
|
|
51
|
-
'google/gemini-2.5-flash-lite-preview-09-2025': { rank: 1 },
|
|
52
|
-
'google/gemini-2.5-flash-preview-04-17': { rank: 1 },
|
|
53
|
-
'google/gemini-2.5-flash-preview-05-20': { rank: 1 },
|
|
54
|
-
'google/gemini-2.5-flash-preview-09-2025': { rank: 1 },
|
|
55
|
-
'google/gemini-2.5-flash-preview-tts': { rank: 1 },
|
|
56
|
-
'google/gemini-2.5-pro': { rank: 1 },
|
|
57
|
-
'google/gemini-2.5-pro-preview-05-06': { rank: 1 },
|
|
58
|
-
'google/gemini-2.5-pro-preview-06-05': { rank: 1 },
|
|
59
|
-
'google/gemini-2.5-pro-preview-tts': { rank: 1 },
|
|
60
|
-
'google/gemini-3-flash-preview': { rank: 1 },
|
|
61
|
-
'google/gemini-3-pro-preview': { rank: 1 },
|
|
62
|
-
'google/gemini-embedding-001': { rank: 1 },
|
|
63
|
-
'google/gemini-flash-latest': { rank: 1 },
|
|
64
|
-
'google/gemini-flash-lite-latest': { rank: 1 },
|
|
65
|
-
'google/gemini-live-2.5-flash': { rank: 1 },
|
|
66
|
-
'google/gemini-live-2.5-flash-preview-native-audio': { rank: 1 },
|
|
67
|
-
'openai/gpt-5.1-codex-max': { rank: 1 },
|
|
68
|
-
'openai/gpt-5.1-codex-mini': { rank: 1 },
|
|
69
|
-
'openai/gpt-5.2': { rank: 1 },
|
|
70
|
-
'openai/gpt-5.2-codex': { rank: 1 },
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const LEVEL_MAPPING: Readonly<Record<ModelLevel, LevelModelSpec>> = {
|
|
74
|
-
level1: { rank: 1, model: null, reasoningEffort: 'low' },
|
|
75
|
-
level2: { rank: 2, model: null, reasoningEffort: 'medium' },
|
|
76
|
-
level3: { rank: 3, model: null, reasoningEffort: 'high' },
|
|
77
|
-
};
|
|
27
|
+
import {
|
|
28
|
+
LEVEL_MAPPING,
|
|
29
|
+
MODEL_CATALOG,
|
|
30
|
+
resolveModelSpec,
|
|
31
|
+
validateConfiguredModelId,
|
|
32
|
+
validateModelId,
|
|
33
|
+
} from './opencode-models';
|
|
78
34
|
|
|
79
35
|
function detectCliFeatures(helpText?: string | null): OpencodeCliFeatures {
|
|
80
36
|
const help = helpText ?? '';
|
|
@@ -244,20 +200,6 @@ function parseEvent(line: string): OutputEvent | null {
|
|
|
244
200
|
return null;
|
|
245
201
|
}
|
|
246
202
|
|
|
247
|
-
function resolveModelSpec(level: ModelLevel, overrides?: LevelOverrides): ResolvedModelSpec {
|
|
248
|
-
return resolveModelSpecWithConfig({
|
|
249
|
-
mapping: LEVEL_MAPPING,
|
|
250
|
-
defaultLevel: 'level2',
|
|
251
|
-
level,
|
|
252
|
-
overrides,
|
|
253
|
-
validateModelId,
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function validateModelId(modelId: string | null | undefined): string | null | undefined {
|
|
258
|
-
return validateModelIdFromCatalog('opencode', MODEL_CATALOG, modelId);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
203
|
function classifyError(error: unknown): ErrorClassification {
|
|
262
204
|
return classifyBaseProviderError(error, [], []);
|
|
263
205
|
}
|
|
@@ -285,5 +227,6 @@ export const opencodeAdapter: ProviderAdapter = {
|
|
|
285
227
|
createParserState: () => createParserState('opencode'),
|
|
286
228
|
resolveModelSpec,
|
|
287
229
|
validateModelId,
|
|
230
|
+
validateConfiguredModelId,
|
|
288
231
|
classifyError,
|
|
289
232
|
};
|
|
@@ -34,6 +34,11 @@ interface RuntimeProviderSettings {
|
|
|
34
34
|
readonly gateway?: GatewayBuildOptions;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
interface RuntimeCommandContext {
|
|
38
|
+
readonly cliFeatures: CliFeatureOverrides;
|
|
39
|
+
readonly authEnv: Readonly<Record<string, string>>;
|
|
40
|
+
}
|
|
41
|
+
|
|
37
42
|
export interface SingleAgentProviderCommandInput {
|
|
38
43
|
readonly provider?: string | null;
|
|
39
44
|
readonly context: string;
|
|
@@ -62,6 +67,7 @@ type MutableModelSpec = {
|
|
|
62
67
|
|
|
63
68
|
const MODEL_LEVELS: readonly ModelLevel[] = ['level1', 'level2', 'level3'];
|
|
64
69
|
const REASONING_EFFORTS: readonly ReasoningEffort[] = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
70
|
+
const LEGACY_ISOLATED_PROVIDER_SETTINGS_ENV = 'ZEROSHOT_ISOLATED_PROVIDER_SETTINGS_JSON';
|
|
65
71
|
const settingsModule: unknown = require('../../lib/settings');
|
|
66
72
|
const providerDetectionModule: unknown = require('../../lib/provider-detection');
|
|
67
73
|
const claudeAuthModule: unknown = require('../../lib/settings/claude-auth');
|
|
@@ -76,6 +82,7 @@ const resolveClaudeAuthFn = moduleFunction(claudeAuthModule, 'resolveClaudeAuth'
|
|
|
76
82
|
export function prepareSingleAgentProviderCommand(
|
|
77
83
|
input: SingleAgentProviderCommandInput
|
|
78
84
|
): PreparedSingleAgentProviderCommand {
|
|
85
|
+
rejectCallerSuppliedModelProvenance(input);
|
|
79
86
|
const baseOptions = input.options ?? {};
|
|
80
87
|
const settings = loadRuntimeSettings();
|
|
81
88
|
const adapter = adapterForRuntimeInput(input.provider, settings);
|
|
@@ -86,7 +93,10 @@ export function prepareSingleAgentProviderCommand(
|
|
|
86
93
|
);
|
|
87
94
|
const cliFeatures = resolveRuntimeCliFeatures(adapter.id, baseOptions.cliFeatures);
|
|
88
95
|
const authEnv = baseOptions.authEnv ?? resolveRuntimeAuthEnv(adapter.id, settings);
|
|
89
|
-
const options = buildRuntimeOptions(baseOptions, adapter, providerSettings,
|
|
96
|
+
const options = buildRuntimeOptions(baseOptions, adapter, providerSettings, {
|
|
97
|
+
cliFeatures,
|
|
98
|
+
authEnv,
|
|
99
|
+
});
|
|
90
100
|
return {
|
|
91
101
|
adapter,
|
|
92
102
|
options,
|
|
@@ -128,8 +138,7 @@ function mergeAcpFailClosedCliFeatures(
|
|
|
128
138
|
...detected,
|
|
129
139
|
...overrides,
|
|
130
140
|
supportsAcpStdio: detected.supportsAcpStdio && overrides.supportsAcpStdio !== false,
|
|
131
|
-
supportsPromptImages:
|
|
132
|
-
detected.supportsPromptImages && overrides.supportsPromptImages !== false,
|
|
141
|
+
supportsPromptImages: detected.supportsPromptImages && overrides.supportsPromptImages !== false,
|
|
133
142
|
supportsLoadSession: detected.supportsLoadSession && overrides.supportsLoadSession !== false,
|
|
134
143
|
supportsSessionCancel:
|
|
135
144
|
detected.supportsSessionCancel && overrides.supportsSessionCancel !== false,
|
|
@@ -162,7 +171,9 @@ export function probeRuntimeProviderCli(provider: string): RuntimeProviderProbe
|
|
|
162
171
|
}
|
|
163
172
|
|
|
164
173
|
const helpText = stringResult(getHelpOutputFn(helpCommand.command, helpCommand.args)).trim();
|
|
165
|
-
const versionText = stringResult(
|
|
174
|
+
const versionText = stringResult(
|
|
175
|
+
getVersionOutputFn(helpCommand.command, helpCommand.args)
|
|
176
|
+
).trim();
|
|
166
177
|
const availabilityProbe = getProviderRegistryEntry(adapter.id).availabilityProbe ?? 'command';
|
|
167
178
|
|
|
168
179
|
return {
|
|
@@ -177,8 +188,7 @@ function buildRuntimeOptions(
|
|
|
177
188
|
baseOptions: BuildProviderCommandOptions,
|
|
178
189
|
adapter: ProviderAdapter,
|
|
179
190
|
providerSettings: RuntimeProviderSettings,
|
|
180
|
-
|
|
181
|
-
authEnv: Readonly<Record<string, string>>
|
|
191
|
+
runtime: RuntimeCommandContext
|
|
182
192
|
): BuildProviderCommandOptions {
|
|
183
193
|
const modelSpec = resolveRuntimeModelSpec(adapter, baseOptions.modelSpec, providerSettings);
|
|
184
194
|
const gateway = resolveRuntimeGatewayOptions(
|
|
@@ -191,16 +201,16 @@ function buildRuntimeOptions(
|
|
|
191
201
|
...baseOptions,
|
|
192
202
|
modelSpec,
|
|
193
203
|
...(gateway === undefined ? {} : { gateway }),
|
|
194
|
-
cliFeatures,
|
|
204
|
+
cliFeatures: runtime.cliFeatures,
|
|
195
205
|
};
|
|
196
206
|
if (baseOptions.jsonSchema && !supportsProviderCapability(adapter.id, 'jsonSchema')) {
|
|
197
|
-
if (!shouldIncludeAuthEnv(baseOptions, authEnv)) {
|
|
207
|
+
if (!shouldIncludeAuthEnv(baseOptions, runtime.authEnv)) {
|
|
198
208
|
return { ...resolved, strictSchema: false };
|
|
199
209
|
}
|
|
200
|
-
return { ...resolved, authEnv, strictSchema: false };
|
|
210
|
+
return { ...resolved, authEnv: runtime.authEnv, strictSchema: false };
|
|
201
211
|
}
|
|
202
|
-
if (!shouldIncludeAuthEnv(baseOptions, authEnv)) return resolved;
|
|
203
|
-
return { ...resolved, authEnv };
|
|
212
|
+
if (!shouldIncludeAuthEnv(baseOptions, runtime.authEnv)) return resolved;
|
|
213
|
+
return { ...resolved, authEnv: runtime.authEnv };
|
|
204
214
|
}
|
|
205
215
|
|
|
206
216
|
function resolveRuntimeGatewayOptions(
|
|
@@ -218,23 +228,19 @@ function resolveRuntimeGatewayOptions(
|
|
|
218
228
|
? settingsGateway.headers
|
|
219
229
|
: { ...(settingsGateway.headers ?? {}), ...requestGateway.headers };
|
|
220
230
|
const mergedGateway: GatewayBuildOptions = {
|
|
221
|
-
...(requestGateway.baseUrl ?? settingsGateway.baseUrl
|
|
231
|
+
...((requestGateway.baseUrl ?? settingsGateway.baseUrl)
|
|
222
232
|
? { baseUrl: requestGateway.baseUrl ?? settingsGateway.baseUrl }
|
|
223
233
|
: {}),
|
|
224
|
-
...(requestGateway.apiKey ?? settingsGateway.apiKey
|
|
234
|
+
...((requestGateway.apiKey ?? settingsGateway.apiKey)
|
|
225
235
|
? { apiKey: requestGateway.apiKey ?? settingsGateway.apiKey }
|
|
226
236
|
: {}),
|
|
227
237
|
...(mergedHeaders === undefined ? {} : { headers: mergedHeaders }),
|
|
228
238
|
model: requestGateway.model ?? modelSpec.model ?? settingsGateway.model ?? null,
|
|
229
|
-
...(requestGateway.toolPolicy ?? settingsGateway.toolPolicy
|
|
239
|
+
...((requestGateway.toolPolicy ?? settingsGateway.toolPolicy)
|
|
230
240
|
? { toolPolicy: requestGateway.toolPolicy ?? settingsGateway.toolPolicy }
|
|
231
241
|
: {}),
|
|
232
242
|
};
|
|
233
|
-
return resolveGatewayConfiguration(
|
|
234
|
-
mergedGateway,
|
|
235
|
-
'options.gateway',
|
|
236
|
-
cwd
|
|
237
|
-
);
|
|
243
|
+
return resolveGatewayConfiguration(mergedGateway, 'options.gateway', cwd);
|
|
238
244
|
}
|
|
239
245
|
|
|
240
246
|
function shouldIncludeAuthEnv(
|
|
@@ -286,7 +292,8 @@ function adapterForRuntimeInput(
|
|
|
286
292
|
provider: string | null | undefined,
|
|
287
293
|
settings: Record<string, unknown>
|
|
288
294
|
): ProviderAdapter {
|
|
289
|
-
const configured =
|
|
295
|
+
const configured =
|
|
296
|
+
provider ?? optionalString(settings.defaultProvider, 'settings.defaultProvider');
|
|
290
297
|
return getProviderAdapter(configured ?? 'claude');
|
|
291
298
|
}
|
|
292
299
|
|
|
@@ -309,16 +316,14 @@ function runtimeProviderSettings(
|
|
|
309
316
|
);
|
|
310
317
|
const gateway =
|
|
311
318
|
provider === 'gateway'
|
|
312
|
-
? normalizeGatewayBuildOptions(
|
|
313
|
-
providerSettings,
|
|
314
|
-
'settings.providerSettings.gateway',
|
|
315
|
-
cwd
|
|
316
|
-
)
|
|
319
|
+
? normalizeGatewayBuildOptions(providerSettings, 'settings.providerSettings.gateway', cwd)
|
|
317
320
|
: undefined;
|
|
318
321
|
if (defaultLevel === undefined) {
|
|
319
322
|
return gateway === undefined ? { levelOverrides } : { levelOverrides, gateway };
|
|
320
323
|
}
|
|
321
|
-
return gateway === undefined
|
|
324
|
+
return gateway === undefined
|
|
325
|
+
? { defaultLevel, levelOverrides }
|
|
326
|
+
: { defaultLevel, levelOverrides, gateway };
|
|
322
327
|
}
|
|
323
328
|
|
|
324
329
|
function runtimeHelpCommand(provider: ProviderId): CommandParts {
|
|
@@ -333,7 +338,11 @@ function probeGatewayProvider(adapter: ProviderAdapter): RuntimeProviderProbe {
|
|
|
333
338
|
try {
|
|
334
339
|
const settings = loadRuntimeSettings();
|
|
335
340
|
const providerSettings = runtimeProviderSettings(settings, 'gateway', process.cwd());
|
|
336
|
-
resolveGatewayConfiguration(
|
|
341
|
+
resolveGatewayConfiguration(
|
|
342
|
+
providerSettings.gateway,
|
|
343
|
+
'settings.providerSettings.gateway',
|
|
344
|
+
process.cwd()
|
|
345
|
+
);
|
|
337
346
|
return {
|
|
338
347
|
available: true,
|
|
339
348
|
helpText: 'Bundled gateway runner',
|
|
@@ -351,8 +360,20 @@ function probeGatewayProvider(adapter: ProviderAdapter): RuntimeProviderProbe {
|
|
|
351
360
|
}
|
|
352
361
|
|
|
353
362
|
function loadRuntimeSettings(): Record<string, unknown> {
|
|
354
|
-
|
|
355
|
-
|
|
363
|
+
if (Object.prototype.hasOwnProperty.call(process.env, LEGACY_ISOLATED_PROVIDER_SETTINGS_ENV)) {
|
|
364
|
+
throw new Error(
|
|
365
|
+
`${LEGACY_ISOLATED_PROVIDER_SETTINGS_ENV} is not a trusted settings channel; use the settings file.`
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
return requiredRecord(loadSettingsFn(), 'loadSettings');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function rejectCallerSuppliedModelProvenance(input: SingleAgentProviderCommandInput): void {
|
|
372
|
+
if (Object.prototype.hasOwnProperty.call(input, 'modelSpecSource')) {
|
|
373
|
+
throw new Error(
|
|
374
|
+
'modelSpecSource is not accepted at the child provider boundary; use modelLevel and effective provider settings.'
|
|
375
|
+
);
|
|
376
|
+
}
|
|
356
377
|
}
|
|
357
378
|
|
|
358
379
|
function moduleFunction(moduleValue: unknown, field: string): UnknownFunction {
|
|
@@ -456,10 +477,7 @@ function requiredRecord(value: unknown, field: string): Record<string, unknown>
|
|
|
456
477
|
throw new Error(`${field} must be an object.`);
|
|
457
478
|
}
|
|
458
479
|
|
|
459
|
-
function stringRecordFromUnknown(
|
|
460
|
-
value: unknown,
|
|
461
|
-
field: string
|
|
462
|
-
): Readonly<Record<string, string>> {
|
|
480
|
+
function stringRecordFromUnknown(value: unknown, field: string): Readonly<Record<string, string>> {
|
|
463
481
|
if (value === undefined || value === null) return {};
|
|
464
482
|
const record = requiredRecord(value, field);
|
|
465
483
|
const result: Record<string, string> = {};
|
|
@@ -367,6 +367,7 @@ export interface ProviderAdapter {
|
|
|
367
367
|
createParserState(): ProviderParserState;
|
|
368
368
|
resolveModelSpec(level: ModelLevel, overrides?: LevelOverrides): ResolvedModelSpec;
|
|
369
369
|
validateModelId(modelId: string | null | undefined): string | null | undefined;
|
|
370
|
+
validateConfiguredModelId?(modelId: string | null | undefined): string | null | undefined;
|
|
370
371
|
classifyError(error: unknown): ErrorClassification;
|
|
371
372
|
}
|
|
372
373
|
|
package/src/agent-wrapper.js
CHANGED
|
@@ -103,10 +103,11 @@ class AgentWrapper {
|
|
|
103
103
|
const taskRunner = options.taskRunner;
|
|
104
104
|
this.mockSpawnFn = (args, { context }) => {
|
|
105
105
|
const spec = this._resolveModelSpec();
|
|
106
|
+
const source = this._resolveModelSpecSource();
|
|
106
107
|
return taskRunner.run(context, {
|
|
107
108
|
agentId: this.id,
|
|
108
|
-
model:
|
|
109
|
-
|
|
109
|
+
...(source === 'direct' ? { model: spec.model } : { modelLevel: spec.level }),
|
|
110
|
+
...(spec.reasoningEffort ? { reasoningEffort: spec.reasoningEffort } : {}),
|
|
110
111
|
provider: this._resolveProvider(),
|
|
111
112
|
cwd: this.config.cwd || process.cwd(),
|
|
112
113
|
worktreePath: this.worktree?.path || null,
|
|
@@ -222,6 +223,17 @@ class AgentWrapper {
|
|
|
222
223
|
return applyReasoningOverride({ ...spec, level }, this.config.reasoningEffort);
|
|
223
224
|
}
|
|
224
225
|
|
|
226
|
+
_resolveModelSpecSource() {
|
|
227
|
+
if (this.modelConfig.type === 'rules') {
|
|
228
|
+
for (const rule of this.modelConfig.rules) {
|
|
229
|
+
if (this._matchesIterationRange(rule.iterations)) {
|
|
230
|
+
return rule.model ? 'direct' : 'provider-level';
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return this.modelConfig.model ? 'direct' : 'provider-level';
|
|
235
|
+
}
|
|
236
|
+
|
|
225
237
|
/**
|
|
226
238
|
* Publish a message to the message bus, always including sender_model
|
|
227
239
|
* @private
|
|
@@ -275,6 +275,7 @@ const hasSufficientEvidence = latestValidatorMessages.every((r) => {
|
|
|
275
275
|
return hasSufficientEvidence;`;
|
|
276
276
|
|
|
277
277
|
const { readRepoSettings } = require('../../lib/repo-settings');
|
|
278
|
+
const { normalizeGitRemoteName, quoteShellArgument } = require('../../lib/git-remote-utils');
|
|
278
279
|
const { resolveRequiredQualityGates } = require('../quality-gates');
|
|
279
280
|
|
|
280
281
|
function getSafeBranchName(value) {
|
|
@@ -313,6 +314,35 @@ function normalizeCloseIssueMode(value) {
|
|
|
313
314
|
return null;
|
|
314
315
|
}
|
|
315
316
|
|
|
317
|
+
function normalizeIssueNumber(value) {
|
|
318
|
+
const candidate = typeof value === 'number' ? String(value) : value;
|
|
319
|
+
if (typeof candidate !== 'string') return 'unknown';
|
|
320
|
+
const trimmed = candidate.trim();
|
|
321
|
+
return /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(trimmed) ? trimmed : 'unknown';
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function normalizeIssueTitle(value) {
|
|
325
|
+
if (typeof value !== 'string' || value.trim() === '') return 'Implementation';
|
|
326
|
+
const normalized = [...value]
|
|
327
|
+
.map((character) => {
|
|
328
|
+
const codePoint = character.codePointAt(0);
|
|
329
|
+
return codePoint < 0x20 || codePoint === 0x7f ? ' ' : character;
|
|
330
|
+
})
|
|
331
|
+
.join('')
|
|
332
|
+
.trim();
|
|
333
|
+
return normalized || 'Implementation';
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function resolveIssueContext(options) {
|
|
337
|
+
const issueNumber = normalizeIssueNumber(options.issueNumber);
|
|
338
|
+
const issueTitle = normalizeIssueTitle(options.issueTitle);
|
|
339
|
+
const issueReference =
|
|
340
|
+
options.includeIssueReference === false || issueNumber === 'unknown'
|
|
341
|
+
? ''
|
|
342
|
+
: `Closes #${issueNumber}`;
|
|
343
|
+
return { issueNumber, issueTitle, issueReference };
|
|
344
|
+
}
|
|
345
|
+
|
|
316
346
|
/**
|
|
317
347
|
* Resolve GitHub configuration from CLI options and repo settings.
|
|
318
348
|
* Priority: CLI options > repo settings (.zeroshot/settings.json) > defaults
|
|
@@ -321,12 +351,20 @@ function normalizeCloseIssueMode(value) {
|
|
|
321
351
|
* @param {string} [options.prBase] - Target branch for PRs
|
|
322
352
|
* @param {boolean} [options.mergeQueue] - Use GitHub merge queue
|
|
323
353
|
* @param {string} [options.closeIssue] - When to close issue: auto|always|never
|
|
354
|
+
* @param {string} [options.gitRemote=origin] - Remote to push the implementation branch to
|
|
355
|
+
* @param {string|number} [options.issueNumber] - Typed issue identifier for prompt commands
|
|
356
|
+
* @param {string} [options.issueTitle] - Typed issue title for prompt commands
|
|
357
|
+
* @param {boolean} [options.includeIssueReference] - Include the closing reference in PR text
|
|
324
358
|
* @returns {Object} Resolved configuration
|
|
325
359
|
*/
|
|
326
360
|
function resolveGitHubConfig(options = {}) {
|
|
327
361
|
const repoSettingsResult = readRepoSettings(options.cwd || process.cwd());
|
|
328
362
|
const repoSettings = repoSettingsResult.settings || {};
|
|
329
363
|
const repoGithub = repoSettings.github || {};
|
|
364
|
+
const gitRemote = normalizeGitRemoteName(options.gitRemote ?? 'origin');
|
|
365
|
+
if (!gitRemote) {
|
|
366
|
+
throw new Error(`Invalid git remote name '${options.gitRemote}'`);
|
|
367
|
+
}
|
|
330
368
|
|
|
331
369
|
// CLI options override repo settings
|
|
332
370
|
const prBase = getSafeBranchName(options.prBase) || getSafeBranchName(repoGithub.prBase);
|
|
@@ -347,7 +385,14 @@ function resolveGitHubConfig(options = {}) {
|
|
|
347
385
|
options.autoMerge === true ||
|
|
348
386
|
(options.autoMerge !== false && parseBool(repoGithub.autoMerge) === true);
|
|
349
387
|
|
|
350
|
-
return {
|
|
388
|
+
return {
|
|
389
|
+
prBase,
|
|
390
|
+
useMergeQueue,
|
|
391
|
+
closeIssueMode,
|
|
392
|
+
autoMerge,
|
|
393
|
+
gitRemote,
|
|
394
|
+
issueContext: resolveIssueContext(options),
|
|
395
|
+
};
|
|
351
396
|
}
|
|
352
397
|
|
|
353
398
|
/**
|
|
@@ -358,13 +403,16 @@ function resolveGitHubConfig(options = {}) {
|
|
|
358
403
|
* @returns {Object|null} Platform configuration or null if unsupported
|
|
359
404
|
*/
|
|
360
405
|
function getPlatformConfig(platform, config = {}) {
|
|
361
|
-
const { prBase, useMergeQueue, closeIssueMode, autoMerge } = config;
|
|
406
|
+
const { prBase, useMergeQueue, closeIssueMode, autoMerge, gitRemote } = config;
|
|
407
|
+
const issueContext = config.issueContext || resolveIssueContext({});
|
|
408
|
+
const issueTitleArgument = quoteShellArgument(`feat: ${issueContext.issueTitle}`);
|
|
409
|
+
const issueReferenceArgument = quoteShellArgument(issueContext.issueReference);
|
|
362
410
|
|
|
363
411
|
const PLATFORM_CONFIGS = {
|
|
364
412
|
github: {
|
|
365
413
|
prName: 'PR',
|
|
366
414
|
prNameLower: 'pull request',
|
|
367
|
-
createCmd: `gh pr create${prBase ? ` --base ${prBase}` : ''} --title
|
|
415
|
+
createCmd: `gh pr create${prBase ? ` --base ${prBase}` : ''} --title ${issueTitleArgument} --body ${issueReferenceArgument}`,
|
|
368
416
|
mergeCmd: useMergeQueue
|
|
369
417
|
? `PR_ID="$(timeout 30 gh pr view --json id --jq .id)"
|
|
370
418
|
gh api graphql -f query='mutation($id:ID!){enqueuePullRequest(input:{pullRequestId:$id}){mergeQueueEntry{state}}}' -f id="$PR_ID"
|
|
@@ -380,24 +428,26 @@ for i in $(seq 1 90); do if timeout 30 gh pr view --json mergedAt --jq .mergedAt
|
|
|
380
428
|
usesMergeQueue: useMergeQueue,
|
|
381
429
|
closeIssueMode: closeIssueMode || 'never',
|
|
382
430
|
autoMerge: Boolean(autoMerge),
|
|
431
|
+
gitRemote,
|
|
432
|
+
issueContext,
|
|
383
433
|
},
|
|
384
434
|
gitlab: {
|
|
385
435
|
prName: 'MR',
|
|
386
436
|
prNameLower: 'merge request',
|
|
387
|
-
createCmd:
|
|
388
|
-
'glab mr create --title "feat: {{issue_title}}" --description "Closes #{{issue_number}}"',
|
|
437
|
+
createCmd: `glab mr create --title ${issueTitleArgument} --description ${issueReferenceArgument}`,
|
|
389
438
|
mergeCmd: 'glab mr merge --auto-merge',
|
|
390
439
|
mergeFallbackCmd: 'glab mr merge',
|
|
391
440
|
prUrlExample: 'https://gitlab.com/owner/repo/-/merge_requests/123',
|
|
392
441
|
outputFields: { urlField: 'mr_url', numberField: 'mr_number', mergedField: 'merged' },
|
|
393
442
|
closeIssueMode: closeIssueMode || 'never',
|
|
394
443
|
autoMerge: Boolean(autoMerge),
|
|
444
|
+
gitRemote,
|
|
445
|
+
issueContext,
|
|
395
446
|
},
|
|
396
447
|
'azure-devops': {
|
|
397
448
|
prName: 'PR',
|
|
398
449
|
prNameLower: 'pull request',
|
|
399
|
-
createCmd:
|
|
400
|
-
'az repos pr create --title "feat: {{issue_title}}" --description "Closes #{{issue_number}}"',
|
|
450
|
+
createCmd: `az repos pr create --title ${issueTitleArgument} --description ${issueReferenceArgument}`,
|
|
401
451
|
mergeCmd: 'az repos pr update --id <PR_ID> --auto-complete true',
|
|
402
452
|
mergeFallbackCmd: 'az repos pr update --id <PR_ID> --status completed',
|
|
403
453
|
prUrlExample: 'https://dev.azure.com/org/project/_git/repo/pullrequest/123',
|
|
@@ -411,6 +461,8 @@ for i in $(seq 1 90); do if timeout 30 gh pr view --json mergedAt --jq .mergedAt
|
|
|
411
461
|
requiresPrIdExtraction: true,
|
|
412
462
|
closeIssueMode: closeIssueMode || 'never',
|
|
413
463
|
autoMerge: Boolean(autoMerge),
|
|
464
|
+
gitRemote,
|
|
465
|
+
issueContext,
|
|
414
466
|
},
|
|
415
467
|
};
|
|
416
468
|
|
|
@@ -430,8 +482,20 @@ const SUPPORTED_PLATFORMS = ['github', 'gitlab', 'azure-devops'];
|
|
|
430
482
|
* @returns {string} The complete review-mode prompt
|
|
431
483
|
*/
|
|
432
484
|
function generateReviewModePrompt(config) {
|
|
433
|
-
const {
|
|
434
|
-
|
|
485
|
+
const {
|
|
486
|
+
prName,
|
|
487
|
+
prNameLower,
|
|
488
|
+
createCmd,
|
|
489
|
+
prUrlExample,
|
|
490
|
+
outputFields,
|
|
491
|
+
requiresPrIdExtraction,
|
|
492
|
+
gitRemote,
|
|
493
|
+
issueContext,
|
|
494
|
+
} = config;
|
|
495
|
+
const gitRemoteArgument = quoteShellArgument(gitRemote);
|
|
496
|
+
const commitMessageArgument = quoteShellArgument(
|
|
497
|
+
`feat: implement #${issueContext.issueNumber} - ${issueContext.issueTitle}`
|
|
498
|
+
);
|
|
435
499
|
|
|
436
500
|
return `CRITICAL: ALL VALIDATORS APPROVED. YOU ARE A TRANSPORT-ONLY GIT PUSHER.
|
|
437
501
|
|
|
@@ -467,13 +531,13 @@ Run this. If nothing to commit, output JSON with ${outputFields.urlField}: null
|
|
|
467
531
|
|
|
468
532
|
### STEP 3: Commit the changes (MANDATORY if there are changes)
|
|
469
533
|
\`\`\`bash
|
|
470
|
-
git commit -m
|
|
534
|
+
git commit -m ${commitMessageArgument}
|
|
471
535
|
\`\`\`
|
|
472
536
|
Run this command. Do not skip it.
|
|
473
537
|
|
|
474
|
-
### STEP 4: Push to
|
|
538
|
+
### STEP 4: Push to ${gitRemote} (MANDATORY)
|
|
475
539
|
\`\`\`bash
|
|
476
|
-
git push -u
|
|
540
|
+
git push -u -- ${gitRemoteArgument} HEAD
|
|
477
541
|
\`\`\`
|
|
478
542
|
Run this. If it fails, do not edit files, rebase, or resolve conflicts. Output blocked JSON with the failure summary.
|
|
479
543
|
|
|
@@ -543,7 +607,14 @@ function generatePrompt(config) {
|
|
|
543
607
|
usesMergeQueue,
|
|
544
608
|
closeIssueMode,
|
|
545
609
|
autoMerge,
|
|
610
|
+
gitRemote,
|
|
611
|
+
issueContext,
|
|
546
612
|
} = config;
|
|
613
|
+
const gitRemoteArgument = quoteShellArgument(gitRemote);
|
|
614
|
+
const issueNumberArgument = quoteShellArgument(issueContext.issueNumber);
|
|
615
|
+
const commitMessageArgument = quoteShellArgument(
|
|
616
|
+
`feat: implement #${issueContext.issueNumber} - ${issueContext.issueTitle}`
|
|
617
|
+
);
|
|
547
618
|
|
|
548
619
|
if (!autoMerge) {
|
|
549
620
|
return generateReviewModePrompt(config);
|
|
@@ -627,13 +698,13 @@ Run this. If nothing to commit, output JSON with ${outputFields.urlField}: null
|
|
|
627
698
|
|
|
628
699
|
### STEP 3: Commit the changes (MANDATORY if there are changes)
|
|
629
700
|
\`\`\`bash
|
|
630
|
-
git commit -m
|
|
701
|
+
git commit -m ${commitMessageArgument}
|
|
631
702
|
\`\`\`
|
|
632
703
|
Run this command. Do not skip it.
|
|
633
704
|
|
|
634
|
-
### STEP 4: Push to
|
|
705
|
+
### STEP 4: Push to ${gitRemote} (MANDATORY)
|
|
635
706
|
\`\`\`bash
|
|
636
|
-
git push -u
|
|
707
|
+
git push -u -- ${gitRemoteArgument} HEAD
|
|
637
708
|
\`\`\`
|
|
638
709
|
Run this. If it fails, do not edit files, rebase, or resolve conflicts. Output blocked JSON with the failure summary.
|
|
639
710
|
|
|
@@ -665,8 +736,8 @@ ${
|
|
|
665
736
|
closeIssueMode !== 'never'
|
|
666
737
|
? `### STEP 7: Close the issue (MANDATORY)
|
|
667
738
|
\`\`\`bash
|
|
668
|
-
if [
|
|
669
|
-
ISSUE_STATE="$(gh issue view {
|
|
739
|
+
if [ ${issueNumberArgument} != "unknown" ]; then
|
|
740
|
+
ISSUE_STATE="$(gh issue view ${issueNumberArgument} --json state --jq .state 2>/dev/null || true)"
|
|
670
741
|
if [ "$ISSUE_STATE" = "OPEN" ]; then
|
|
671
742
|
BASE_BRANCH="${rebaseBranch || 'main'}"
|
|
672
743
|
DEFAULT_BRANCH="$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name 2>/dev/null || true)"
|
|
@@ -682,9 +753,9 @@ if [ "{{issue_number}}" != "unknown" ]; then
|
|
|
682
753
|
if [ "$SHOULD_CLOSE" = "1" ]; then
|
|
683
754
|
PR_URL="$(gh pr view --json url --jq .url 2>/dev/null || true)"
|
|
684
755
|
if [ -n "$PR_URL" ]; then
|
|
685
|
-
gh issue close {
|
|
756
|
+
gh issue close ${issueNumberArgument} --comment "Implemented in $PR_URL"
|
|
686
757
|
else
|
|
687
|
-
gh issue close {
|
|
758
|
+
gh issue close ${issueNumberArgument} --comment "Implemented"
|
|
688
759
|
fi
|
|
689
760
|
fi
|
|
690
761
|
fi
|
|
@@ -728,6 +799,10 @@ If blocked before creating a ${prName}, output:
|
|
|
728
799
|
* @param {string} [options.prBase] - Target branch for PRs
|
|
729
800
|
* @param {boolean} [options.mergeQueue] - Use GitHub merge queue
|
|
730
801
|
* @param {string} [options.closeIssue] - When to close issue: auto|always|never
|
|
802
|
+
* @param {string} [options.gitRemote=origin] - Remote to push the implementation branch to
|
|
803
|
+
* @param {string|number} [options.issueNumber] - Typed issue identifier for prompt commands
|
|
804
|
+
* @param {string} [options.issueTitle] - Typed issue title for prompt commands
|
|
805
|
+
* @param {boolean} [options.includeIssueReference] - Include the closing reference in PR text
|
|
731
806
|
* @param {Array} [options.requiredQualityGates] - Required handoff quality gates
|
|
732
807
|
* @param {boolean} [options.autoMerge] - Merge the PR (--ship). False stops after PR creation (--pr).
|
|
733
808
|
* @returns {Object} Agent configuration object
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Socket Discovery - Utilities for socket path management
|
|
3
3
|
*
|
|
4
|
-
* Socket locations
|
|
5
|
-
*
|
|
6
|
-
* - Clusters: ~/.zeroshot/sockets/cluster-<id>.sock (cluster-level, future)
|
|
7
|
-
* - Agents: ~/.zeroshot/sockets/cluster-<id>/<agent-id>.sock
|
|
4
|
+
* Socket locations are allocated by socket-paths.js under a short, per-user
|
|
5
|
+
* runtime directory so long HOME paths cannot exceed Unix socket limits.
|
|
8
6
|
*/
|
|
9
7
|
|
|
10
8
|
const path = require('path');
|
|
11
9
|
const fs = require('fs');
|
|
12
|
-
const os = require('os');
|
|
13
10
|
const net = require('net');
|
|
14
11
|
const { readClustersFileSync } = require('../../lib/clusters-registry');
|
|
12
|
+
const socketPaths = require('./socket-paths');
|
|
15
13
|
|
|
16
|
-
const ZEROSHOT_DIR = path.join(
|
|
17
|
-
const SOCKET_DIR =
|
|
14
|
+
const ZEROSHOT_DIR = path.join(socketPaths.resolveHomeDir(), '.zeroshot');
|
|
15
|
+
const SOCKET_DIR = socketPaths.getSocketDir();
|
|
18
16
|
|
|
19
17
|
/**
|
|
20
18
|
* Check if an ID is a known cluster by looking up clusters.json
|
|
@@ -35,9 +33,7 @@ function isKnownCluster(id) {
|
|
|
35
33
|
* Ensure socket directory exists
|
|
36
34
|
*/
|
|
37
35
|
function ensureSocketDir() {
|
|
38
|
-
|
|
39
|
-
fs.mkdirSync(SOCKET_DIR, { recursive: true });
|
|
40
|
-
}
|
|
36
|
+
socketPaths.ensureSocketDir();
|
|
41
37
|
}
|
|
42
38
|
|
|
43
39
|
/**
|
|
@@ -46,8 +42,7 @@ function ensureSocketDir() {
|
|
|
46
42
|
* @returns {string} - Socket path
|
|
47
43
|
*/
|
|
48
44
|
function getTaskSocketPath(taskId) {
|
|
49
|
-
|
|
50
|
-
return path.join(SOCKET_DIR, `${taskId}.sock`);
|
|
45
|
+
return socketPaths.getTaskSocketPath(taskId);
|
|
51
46
|
}
|
|
52
47
|
|
|
53
48
|
/**
|
|
@@ -57,11 +52,7 @@ function getTaskSocketPath(taskId) {
|
|
|
57
52
|
* @returns {string} - Socket path
|
|
58
53
|
*/
|
|
59
54
|
function getAgentSocketPath(clusterId, agentId) {
|
|
60
|
-
|
|
61
|
-
if (!fs.existsSync(clusterDir)) {
|
|
62
|
-
fs.mkdirSync(clusterDir, { recursive: true });
|
|
63
|
-
}
|
|
64
|
-
return path.join(clusterDir, `${agentId}.sock`);
|
|
55
|
+
return socketPaths.getAgentSocketPath(clusterId, agentId);
|
|
65
56
|
}
|
|
66
57
|
|
|
67
58
|
/**
|
|
@@ -81,8 +72,7 @@ function getSocketPath(id, agentId = null) {
|
|
|
81
72
|
return getAgentSocketPath(id, agentId);
|
|
82
73
|
}
|
|
83
74
|
// Cluster-level socket (future use)
|
|
84
|
-
|
|
85
|
-
return path.join(SOCKET_DIR, `${id}.sock`);
|
|
75
|
+
return socketPaths.getClusterSocketPath(id);
|
|
86
76
|
}
|
|
87
77
|
// Unknown format, treat as task
|
|
88
78
|
return getTaskSocketPath(id);
|