snipara-companion 3.4.0 → 3.5.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/CHANGELOG.md +16 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +187 -13
- package/docs/FULL_REFERENCE.md +47 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
Release notes for `snipara-companion`, newest first.
|
|
4
4
|
|
|
5
|
+
## New In 3.5.0
|
|
6
|
+
|
|
7
|
+
- Adds an explicit `workflow run --strong-repair` handoff contract for one
|
|
8
|
+
bounded strong-adapter repair after local proof or output failure.
|
|
9
|
+
- Keeps Companion recommendation-only: it records final authority, proof,
|
|
10
|
+
scope, and `main_agent` fallback without silently launching workers.
|
|
11
|
+
|
|
12
|
+
## New In 3.4.1
|
|
13
|
+
|
|
14
|
+
- Declares provider API keys by environment-variable name and supports
|
|
15
|
+
`authorization` or `x-api-key` headers without persisting secret values.
|
|
16
|
+
- Resolves declared Codex and Claude CLI workers to explicit native adapters;
|
|
17
|
+
unsupported generic CLI profiles fail closed.
|
|
18
|
+
- Preserves opt-in local routing behavior while forwarding provider auth metadata
|
|
19
|
+
to the orchestrator catalog.
|
|
20
|
+
|
|
5
21
|
## New In 3.4.0
|
|
6
22
|
|
|
7
23
|
- Adds repeatable `workers execute --output-fragment` contracts.
|
package/dist/index.d.ts
CHANGED
|
@@ -3238,6 +3238,14 @@ interface AdaptiveRoutingResolution {
|
|
|
3238
3238
|
warnings?: string[];
|
|
3239
3239
|
rejectedReasons?: Record<string, string[]>;
|
|
3240
3240
|
}
|
|
3241
|
+
interface AdaptiveStrongRepairContract {
|
|
3242
|
+
enabled: boolean;
|
|
3243
|
+
maxAttempts: 1;
|
|
3244
|
+
finalAuthority: "strong_adapter";
|
|
3245
|
+
proofRequired: true;
|
|
3246
|
+
scopePreserved: true;
|
|
3247
|
+
fallback: "main_agent";
|
|
3248
|
+
}
|
|
3241
3249
|
interface AdaptiveWorkRoutingRecommendation {
|
|
3242
3250
|
workProfile: AdaptiveWorkProfile;
|
|
3243
3251
|
requirements: AdaptiveModelRequirements;
|
|
@@ -3245,6 +3253,7 @@ interface AdaptiveWorkRoutingRecommendation {
|
|
|
3245
3253
|
gateway?: AdaptiveRoutingGatewayStatus;
|
|
3246
3254
|
runtimeCatalog?: AdaptiveRoutingRuntimeCatalog;
|
|
3247
3255
|
resolution?: AdaptiveRoutingResolution;
|
|
3256
|
+
strongRepair?: AdaptiveStrongRepairContract;
|
|
3248
3257
|
}
|
|
3249
3258
|
interface AdaptiveWorkRoutingOptions {
|
|
3250
3259
|
query: string;
|
|
@@ -3259,6 +3268,7 @@ interface AdaptiveWorkRoutingOptions {
|
|
|
3259
3268
|
catalogLimit?: number;
|
|
3260
3269
|
dailyBudgetCents?: number;
|
|
3261
3270
|
monthlyBudgetCents?: number;
|
|
3271
|
+
strongRepair?: boolean;
|
|
3262
3272
|
}
|
|
3263
3273
|
interface OrchestratorHandoffArtifact {
|
|
3264
3274
|
schemaVersion: "snipara.orchestrator.handoff.v1";
|
|
@@ -3289,6 +3299,7 @@ interface OrchestratorHandoffArtifact {
|
|
|
3289
3299
|
gateway?: AdaptiveRoutingGatewayStatus;
|
|
3290
3300
|
runtimeCatalog?: AdaptiveRoutingRuntimeCatalog;
|
|
3291
3301
|
resolution?: AdaptiveRoutingResolution;
|
|
3302
|
+
strongRepair?: AdaptiveStrongRepairContract;
|
|
3292
3303
|
};
|
|
3293
3304
|
task: {
|
|
3294
3305
|
title: string;
|
|
@@ -4478,6 +4489,8 @@ interface LocalWorkerDeclaration {
|
|
|
4478
4489
|
endpointType: "local";
|
|
4479
4490
|
provider: string;
|
|
4480
4491
|
baseUrl: string;
|
|
4492
|
+
apiKeyEnv?: string;
|
|
4493
|
+
apiKeyHeader?: "authorization" | "x-api-key";
|
|
4481
4494
|
command?: string;
|
|
4482
4495
|
model?: string;
|
|
4483
4496
|
preferModel?: string;
|
|
@@ -4496,11 +4509,16 @@ interface LocalWorkersConfig {
|
|
|
4496
4509
|
interface LocalWorkerRoutingDefaults {
|
|
4497
4510
|
worker: LocalWorkerDeclaration;
|
|
4498
4511
|
routeLocalWorkers: true;
|
|
4512
|
+
routingLocalTransport: "openai_http" | "cli";
|
|
4513
|
+
routingLocalAdapter?: "codex_app_server" | "claude_cli";
|
|
4514
|
+
routingLocalCommand?: string;
|
|
4499
4515
|
routingWorkerRole: string;
|
|
4500
4516
|
routingLocalBaseUrl: string;
|
|
4501
4517
|
routingLocalModel?: string;
|
|
4502
4518
|
routingLocalPreferModel?: string;
|
|
4503
4519
|
routingLocalProvider: string;
|
|
4520
|
+
routingLocalApiKeyEnv?: string;
|
|
4521
|
+
routingLocalApiKeyHeader?: "authorization" | "x-api-key";
|
|
4504
4522
|
routingPreferredEndpoints: string[];
|
|
4505
4523
|
routingAllowedEndpoints: string[];
|
|
4506
4524
|
plannerRetainsReasoning: true;
|
|
@@ -4520,6 +4538,8 @@ interface LocalWorkerAddOptions {
|
|
|
4520
4538
|
json?: boolean;
|
|
4521
4539
|
transport?: "openai_http" | "cli";
|
|
4522
4540
|
command?: string;
|
|
4541
|
+
apiKeyEnv?: string;
|
|
4542
|
+
apiKeyHeader?: "authorization" | "x-api-key";
|
|
4523
4543
|
}
|
|
4524
4544
|
interface LocalWorkerStatusOptions {
|
|
4525
4545
|
json?: boolean;
|
|
@@ -4542,6 +4562,8 @@ interface LocalWorkerProbeOptions {
|
|
|
4542
4562
|
writeScope?: string[];
|
|
4543
4563
|
reasoning?: "low" | "medium" | "high";
|
|
4544
4564
|
contextWindow?: number;
|
|
4565
|
+
apiKeyEnv?: string;
|
|
4566
|
+
apiKeyHeader?: "authorization" | "x-api-key";
|
|
4545
4567
|
json?: boolean;
|
|
4546
4568
|
}
|
|
4547
4569
|
declare function workersLocalAddCommand(options: LocalWorkerAddOptions): void;
|
package/dist/index.js
CHANGED
|
@@ -9755,7 +9755,9 @@ function normalizeWorkerProfileTransport(value) {
|
|
|
9755
9755
|
baseUrl,
|
|
9756
9756
|
...stringValue(value.model) ? { model: stringValue(value.model) } : {},
|
|
9757
9757
|
...stringValue(value.preferModel) ? { preferModel: stringValue(value.preferModel) } : {},
|
|
9758
|
-
...stringValue(value.provider) ? { provider: stringValue(value.provider) } : {}
|
|
9758
|
+
...stringValue(value.provider) ? { provider: stringValue(value.provider) } : {},
|
|
9759
|
+
...safeEnvironmentVariableName(value.apiKeyEnv) ? { apiKeyEnv: safeEnvironmentVariableName(value.apiKeyEnv) } : {},
|
|
9760
|
+
...value.apiKeyHeader === "authorization" || value.apiKeyHeader === "x-api-key" ? { apiKeyHeader: value.apiKeyHeader } : {}
|
|
9759
9761
|
};
|
|
9760
9762
|
}
|
|
9761
9763
|
if (kind === "cli") {
|
|
@@ -9897,6 +9899,10 @@ function numberValue(value) {
|
|
|
9897
9899
|
function booleanValue(value) {
|
|
9898
9900
|
return value === true;
|
|
9899
9901
|
}
|
|
9902
|
+
function safeEnvironmentVariableName(value) {
|
|
9903
|
+
const normalized = stringValue(value);
|
|
9904
|
+
return normalized && /^[A-Za-z_][A-Za-z0-9_]*$/.test(normalized) ? normalized : void 0;
|
|
9905
|
+
}
|
|
9900
9906
|
function stringValue(value) {
|
|
9901
9907
|
if (value === null || value === void 0) {
|
|
9902
9908
|
return void 0;
|
|
@@ -14201,7 +14207,8 @@ function buildOrchestratorHandoff(options) {
|
|
|
14201
14207
|
routingCard: options.adaptiveRouting.routingCard,
|
|
14202
14208
|
...options.adaptiveRouting.gateway ? { gateway: options.adaptiveRouting.gateway } : {},
|
|
14203
14209
|
...options.adaptiveRouting.runtimeCatalog ? { runtimeCatalog: options.adaptiveRouting.runtimeCatalog } : {},
|
|
14204
|
-
...options.adaptiveRouting.resolution ? { resolution: options.adaptiveRouting.resolution } : {}
|
|
14210
|
+
...options.adaptiveRouting.resolution ? { resolution: options.adaptiveRouting.resolution } : {},
|
|
14211
|
+
...options.adaptiveRouting.strongRepair ? { strongRepair: options.adaptiveRouting.strongRepair } : {}
|
|
14205
14212
|
} : {}
|
|
14206
14213
|
},
|
|
14207
14214
|
task: {
|
|
@@ -14318,7 +14325,17 @@ function buildAdaptiveWorkRoutingRecommendation(options) {
|
|
|
14318
14325
|
return {
|
|
14319
14326
|
workProfile,
|
|
14320
14327
|
requirements,
|
|
14321
|
-
routingCard
|
|
14328
|
+
routingCard,
|
|
14329
|
+
...options.strongRepair ? {
|
|
14330
|
+
strongRepair: {
|
|
14331
|
+
enabled: true,
|
|
14332
|
+
maxAttempts: 1,
|
|
14333
|
+
finalAuthority: "strong_adapter",
|
|
14334
|
+
proofRequired: true,
|
|
14335
|
+
scopePreserved: true,
|
|
14336
|
+
fallback: "main_agent"
|
|
14337
|
+
}
|
|
14338
|
+
} : {}
|
|
14322
14339
|
};
|
|
14323
14340
|
}
|
|
14324
14341
|
function inferAdaptiveTaskType(query, changedFiles) {
|
|
@@ -19627,6 +19644,9 @@ function workersLocalAddCommand(options) {
|
|
|
19627
19644
|
printKeyValue("Role:", result.worker.workerRole);
|
|
19628
19645
|
printKeyValue("Provider:", result.worker.provider);
|
|
19629
19646
|
printKeyValue("Base URL:", result.worker.baseUrl);
|
|
19647
|
+
if (result.worker.apiKeyEnv) {
|
|
19648
|
+
printKeyValue("API key env:", result.worker.apiKeyEnv);
|
|
19649
|
+
}
|
|
19630
19650
|
if (result.worker.model) {
|
|
19631
19651
|
printKeyValue("Model:", result.worker.model);
|
|
19632
19652
|
} else if (result.worker.preferModel) {
|
|
@@ -19718,7 +19738,9 @@ function workersLocalProbeCommand(options) {
|
|
|
19718
19738
|
capabilities: normalizeStringList3(options.capabilities),
|
|
19719
19739
|
writeScope: normalizeStringList3(options.writeScope),
|
|
19720
19740
|
model,
|
|
19721
|
-
preferModel
|
|
19741
|
+
preferModel,
|
|
19742
|
+
apiKeyEnv: options.apiKeyEnv,
|
|
19743
|
+
apiKeyHeader: options.apiKeyHeader
|
|
19722
19744
|
});
|
|
19723
19745
|
const catalogContextWindow = resolveContextWindowFromCatalog(catalog, model, preferModel);
|
|
19724
19746
|
const contextWindow = normalizeContextWindow(
|
|
@@ -19737,7 +19759,9 @@ function workersLocalProbeCommand(options) {
|
|
|
19737
19759
|
baseUrl,
|
|
19738
19760
|
...model ? { model } : {},
|
|
19739
19761
|
...preferModel ? { preferModel } : {},
|
|
19740
|
-
provider
|
|
19762
|
+
provider,
|
|
19763
|
+
...options.apiKeyEnv ? { apiKeyEnv: options.apiKeyEnv } : {},
|
|
19764
|
+
...options.apiKeyHeader ? { apiKeyHeader: options.apiKeyHeader } : {}
|
|
19741
19765
|
},
|
|
19742
19766
|
capabilities: {
|
|
19743
19767
|
roles: [role],
|
|
@@ -19891,7 +19915,26 @@ function resolveLocalWorkerRoutingDefaults(options = {}) {
|
|
|
19891
19915
|
throw new Error(`Local worker ${requestedId} is not declared.`);
|
|
19892
19916
|
}
|
|
19893
19917
|
if (!worker.baseUrl || worker.baseUrl === "cli://command") {
|
|
19894
|
-
|
|
19918
|
+
const command = worker.command?.toLowerCase() ?? "";
|
|
19919
|
+
const adapter = command.includes("codex") ? "codex_app_server" : command.includes("claude") ? "claude_cli" : void 0;
|
|
19920
|
+
if (!adapter) {
|
|
19921
|
+
throw new Error(
|
|
19922
|
+
`CLI worker ${worker.id} is not mapped to a supported native adapter; use a Codex or Claude command.`
|
|
19923
|
+
);
|
|
19924
|
+
}
|
|
19925
|
+
return {
|
|
19926
|
+
worker,
|
|
19927
|
+
routeLocalWorkers: true,
|
|
19928
|
+
routingLocalTransport: "cli",
|
|
19929
|
+
routingLocalBaseUrl: "cli://command",
|
|
19930
|
+
routingLocalAdapter: adapter,
|
|
19931
|
+
routingLocalCommand: worker.command,
|
|
19932
|
+
routingWorkerRole: worker.workerRole,
|
|
19933
|
+
routingLocalProvider: worker.provider,
|
|
19934
|
+
routingPreferredEndpoints: ["local"],
|
|
19935
|
+
routingAllowedEndpoints: ["local"],
|
|
19936
|
+
plannerRetainsReasoning: true
|
|
19937
|
+
};
|
|
19895
19938
|
}
|
|
19896
19939
|
if (!worker.model && !worker.preferModel) {
|
|
19897
19940
|
const hasCatalogHint = true;
|
|
@@ -19900,11 +19943,14 @@ function resolveLocalWorkerRoutingDefaults(options = {}) {
|
|
|
19900
19943
|
return {
|
|
19901
19944
|
worker,
|
|
19902
19945
|
routeLocalWorkers: true,
|
|
19946
|
+
routingLocalTransport: "openai_http",
|
|
19903
19947
|
routingWorkerRole: worker.workerRole,
|
|
19904
19948
|
routingLocalBaseUrl: worker.baseUrl,
|
|
19905
19949
|
...worker.model ? { routingLocalModel: worker.model } : {},
|
|
19906
19950
|
...worker.preferModel ? { routingLocalPreferModel: worker.preferModel } : {},
|
|
19907
19951
|
routingLocalProvider: worker.provider,
|
|
19952
|
+
...worker.apiKeyEnv ? { routingLocalApiKeyEnv: worker.apiKeyEnv } : {},
|
|
19953
|
+
...worker.apiKeyHeader ? { routingLocalApiKeyHeader: worker.apiKeyHeader } : {},
|
|
19908
19954
|
routingPreferredEndpoints: ["local"],
|
|
19909
19955
|
routingAllowedEndpoints: ["local"],
|
|
19910
19956
|
plannerRetainsReasoning: true
|
|
@@ -20012,7 +20058,9 @@ function normalizeWorkerProfileRecord(value) {
|
|
|
20012
20058
|
transport: {
|
|
20013
20059
|
...transport,
|
|
20014
20060
|
baseUrl,
|
|
20015
|
-
provider: normalizeProvider(transport.provider ?? "lm-studio")
|
|
20061
|
+
provider: normalizeProvider(transport.provider ?? "lm-studio"),
|
|
20062
|
+
...transport.apiKeyEnv ? { apiKeyEnv: transport.apiKeyEnv } : {},
|
|
20063
|
+
...transport.apiKeyHeader ? { apiKeyHeader: transport.apiKeyHeader } : {}
|
|
20016
20064
|
}
|
|
20017
20065
|
};
|
|
20018
20066
|
}
|
|
@@ -20047,7 +20095,9 @@ function toProfileFromLegacyWorker(worker) {
|
|
|
20047
20095
|
baseUrl: worker.baseUrl,
|
|
20048
20096
|
...worker.model ? { model: worker.model } : {},
|
|
20049
20097
|
...worker.preferModel ? { preferModel: worker.preferModel } : {},
|
|
20050
|
-
provider: worker.provider
|
|
20098
|
+
provider: worker.provider,
|
|
20099
|
+
...worker.apiKeyEnv ? { apiKeyEnv: worker.apiKeyEnv } : {},
|
|
20100
|
+
...worker.apiKeyHeader ? { apiKeyHeader: worker.apiKeyHeader } : {}
|
|
20051
20101
|
},
|
|
20052
20102
|
capabilities: {
|
|
20053
20103
|
roles: [normalizeWorkerRole(worker.workerRole)],
|
|
@@ -20090,6 +20140,8 @@ function toLocalWorkerDeclaration(profile) {
|
|
|
20090
20140
|
endpointType: "local",
|
|
20091
20141
|
provider: normalizeProvider(profile.transport.provider ?? "lm-studio"),
|
|
20092
20142
|
baseUrl: profile.transport.baseUrl,
|
|
20143
|
+
...profile.transport.apiKeyEnv ? { apiKeyEnv: profile.transport.apiKeyEnv } : {},
|
|
20144
|
+
...profile.transport.apiKeyHeader ? { apiKeyHeader: profile.transport.apiKeyHeader } : {},
|
|
20093
20145
|
...stringValue6(profile.transport.model) ? { model: stringValue6(profile.transport.model) } : {},
|
|
20094
20146
|
...stringValue6(profile.transport.preferModel) && !profile.transport.model ? { preferModel: stringValue6(profile.transport.preferModel) } : {},
|
|
20095
20147
|
capabilities: normalizeWorkerCapabilities(profile.capabilities.roles),
|
|
@@ -20195,6 +20247,12 @@ function runOrchestratorLocalCatalog(options) {
|
|
|
20195
20247
|
if (options.preferModel) {
|
|
20196
20248
|
args.push("--prefer-model", options.preferModel);
|
|
20197
20249
|
}
|
|
20250
|
+
if (options.apiKeyEnv) {
|
|
20251
|
+
args.push("--api-key-env", options.apiKeyEnv);
|
|
20252
|
+
}
|
|
20253
|
+
if (options.apiKeyHeader) {
|
|
20254
|
+
args.push("--api-key-header", options.apiKeyHeader);
|
|
20255
|
+
}
|
|
20198
20256
|
try {
|
|
20199
20257
|
const result = (0, import_node_child_process9.execFileSync)("snipara-orchestrator", args, {
|
|
20200
20258
|
encoding: "utf8",
|
|
@@ -20330,12 +20388,15 @@ function normalizeWorkerTransport(options) {
|
|
|
20330
20388
|
const baseUrl = normalizeBaseUrl(options.baseUrl ?? "http://127.0.0.1:1234");
|
|
20331
20389
|
const model = stringValue6(options.model);
|
|
20332
20390
|
const preferModel = stringValue6(options.preferModel);
|
|
20391
|
+
const apiKeyEnv = normalizeApiKeyEnvironmentName(options.apiKeyEnv);
|
|
20333
20392
|
return {
|
|
20334
20393
|
kind: "openai_http",
|
|
20335
20394
|
baseUrl,
|
|
20336
20395
|
...model ? { model } : {},
|
|
20337
20396
|
...preferModel ? { preferModel } : {},
|
|
20338
|
-
provider: normalizeProvider(stringValue6(options.provider) ?? "lm-studio")
|
|
20397
|
+
provider: normalizeProvider(stringValue6(options.provider) ?? "lm-studio"),
|
|
20398
|
+
...apiKeyEnv ? { apiKeyEnv } : {},
|
|
20399
|
+
...options.apiKeyHeader ? { apiKeyHeader: options.apiKeyHeader } : {}
|
|
20339
20400
|
};
|
|
20340
20401
|
}
|
|
20341
20402
|
function normalizeProvider(value) {
|
|
@@ -20357,6 +20418,8 @@ function normalizeLegacyWorkerDeclaration(value) {
|
|
|
20357
20418
|
endpointType: "local",
|
|
20358
20419
|
provider: normalizeProvider(stringValue6(value.provider) ?? "lm-studio"),
|
|
20359
20420
|
baseUrl,
|
|
20421
|
+
...stringValue6(value.apiKeyEnv) ? { apiKeyEnv: stringValue6(value.apiKeyEnv) } : {},
|
|
20422
|
+
...value.apiKeyHeader === "authorization" || value.apiKeyHeader === "x-api-key" ? { apiKeyHeader: value.apiKeyHeader } : {},
|
|
20360
20423
|
...stringValue6(value.model) ? { model: stringValue6(value.model) } : {},
|
|
20361
20424
|
...stringValue6(value.preferModel) ? { preferModel: stringValue6(value.preferModel) } : {},
|
|
20362
20425
|
capabilities: normalizeStringList3(value.capabilities).length > 0 ? normalizeStringList3(value.capabilities) : defaultCapabilitiesForWorker(normalizeWorkerRole(value.workerRole)),
|
|
@@ -20434,6 +20497,16 @@ function stringValue6(value) {
|
|
|
20434
20497
|
const stringified = String(value).trim();
|
|
20435
20498
|
return stringified || void 0;
|
|
20436
20499
|
}
|
|
20500
|
+
function normalizeApiKeyEnvironmentName(value) {
|
|
20501
|
+
const normalized = stringValue6(value);
|
|
20502
|
+
if (!normalized) {
|
|
20503
|
+
return void 0;
|
|
20504
|
+
}
|
|
20505
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(normalized)) {
|
|
20506
|
+
throw new Error("API key env must be a shell-safe environment variable name.");
|
|
20507
|
+
}
|
|
20508
|
+
return normalized;
|
|
20509
|
+
}
|
|
20437
20510
|
function isRecord10(value) {
|
|
20438
20511
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20439
20512
|
}
|
|
@@ -25105,6 +25178,10 @@ function printAdaptiveRoutingRecommendation(routing) {
|
|
|
25105
25178
|
if (routing.requirements.plannerRetainsReasoning) {
|
|
25106
25179
|
printKeyValue2("Planner retains reasoning:", "yes");
|
|
25107
25180
|
}
|
|
25181
|
+
if (routing.strongRepair?.enabled) {
|
|
25182
|
+
printKeyValue2("Strong repair:", "one bounded attempt after proof/output failure");
|
|
25183
|
+
printKeyValue2("Repair authority:", routing.strongRepair.finalAuthority);
|
|
25184
|
+
}
|
|
25108
25185
|
if (routing.gateway) {
|
|
25109
25186
|
printKeyValue2(
|
|
25110
25187
|
routing.gateway.source === "local_orchestrator" ? "Local route:" : "Hosted catalog:",
|
|
@@ -25425,7 +25502,7 @@ function shouldResolveAdaptiveRoutingLocally(options, routing) {
|
|
|
25425
25502
|
return preferred.includes("local") || allowed.length === 0 || allowed.includes("local");
|
|
25426
25503
|
}
|
|
25427
25504
|
function hasLocalRoutingRequest(options) {
|
|
25428
|
-
return options.routeLocalWorkers === true || Boolean(options.routingLocalWorker) || Boolean(options.routingLocalBaseUrl) || Boolean(options.routingLocalModel) || Boolean(options.routingLocalPreferModel) || Boolean(options.routingLocalProvider) || normalizeRoutingEndpointTypes(options.routingPreferredEndpoints).includes("local") || normalizeRoutingEndpointTypes(options.routingAllowedEndpoints).includes("local");
|
|
25505
|
+
return options.routeLocalWorkers === true || Boolean(options.routingLocalWorker) || Boolean(options.routingLocalBaseUrl) || Boolean(options.routingLocalModel) || Boolean(options.routingLocalPreferModel) || Boolean(options.routingLocalProvider) || Boolean(options.routingLocalApiKeyEnv) || normalizeRoutingEndpointTypes(options.routingPreferredEndpoints).includes("local") || normalizeRoutingEndpointTypes(options.routingAllowedEndpoints).includes("local");
|
|
25429
25506
|
}
|
|
25430
25507
|
function applyLocalWorkerRoutingDefaults(options, defaults) {
|
|
25431
25508
|
if (!defaults) {
|
|
@@ -25434,6 +25511,9 @@ function applyLocalWorkerRoutingDefaults(options, defaults) {
|
|
|
25434
25511
|
return {
|
|
25435
25512
|
...options,
|
|
25436
25513
|
routeLocalWorkers: options.routeLocalWorkers ?? defaults.routeLocalWorkers,
|
|
25514
|
+
routingLocalTransport: options.routingLocalTransport ?? defaults.routingLocalTransport,
|
|
25515
|
+
routingLocalAdapter: options.routingLocalAdapter ?? defaults.routingLocalAdapter,
|
|
25516
|
+
routingLocalCommand: options.routingLocalCommand ?? defaults.routingLocalCommand,
|
|
25437
25517
|
routingWorkerRole: options.routingWorkerRole ?? defaults.routingWorkerRole,
|
|
25438
25518
|
routingPreferredEndpoints: options.routingPreferredEndpoints && options.routingPreferredEndpoints.length > 0 ? options.routingPreferredEndpoints : defaults.routingPreferredEndpoints,
|
|
25439
25519
|
routingAllowedEndpoints: options.routingAllowedEndpoints && options.routingAllowedEndpoints.length > 0 ? options.routingAllowedEndpoints : defaults.routingAllowedEndpoints,
|
|
@@ -25441,6 +25521,8 @@ function applyLocalWorkerRoutingDefaults(options, defaults) {
|
|
|
25441
25521
|
routingLocalModel: options.routingLocalModel ?? defaults.routingLocalModel,
|
|
25442
25522
|
routingLocalPreferModel: options.routingLocalPreferModel ?? defaults.routingLocalPreferModel,
|
|
25443
25523
|
routingLocalProvider: options.routingLocalProvider ?? defaults.routingLocalProvider,
|
|
25524
|
+
routingLocalApiKeyEnv: options.routingLocalApiKeyEnv ?? defaults.routingLocalApiKeyEnv,
|
|
25525
|
+
routingLocalApiKeyHeader: options.routingLocalApiKeyHeader ?? defaults.routingLocalApiKeyHeader,
|
|
25444
25526
|
plannerRetainsReasoning: options.plannerRetainsReasoning ?? defaults.plannerRetainsReasoning
|
|
25445
25527
|
};
|
|
25446
25528
|
}
|
|
@@ -25455,6 +25537,67 @@ function runOrchestratorJsonCommand(args, cwd = process.cwd()) {
|
|
|
25455
25537
|
}
|
|
25456
25538
|
function enrichAdaptiveRoutingWithLocalOrchestrator(routing, options, cwd = process.cwd()) {
|
|
25457
25539
|
try {
|
|
25540
|
+
if (options.routingLocalTransport === "cli") {
|
|
25541
|
+
const candidateId = options.routingLocalAdapter ?? options.routingLocalCommand ?? "declared-cli";
|
|
25542
|
+
const candidate = {
|
|
25543
|
+
candidateId,
|
|
25544
|
+
workerClass: routing.requirements.workerRole,
|
|
25545
|
+
catalogSource: "declared_cli_worker",
|
|
25546
|
+
endpointType: "local",
|
|
25547
|
+
workerRoles: [routing.requirements.workerRole],
|
|
25548
|
+
capabilities: routing.requirements.capabilities ?? [],
|
|
25549
|
+
writeScope: routing.requirements.writeScope ?? [],
|
|
25550
|
+
reasoning: routing.requirements.reasoning,
|
|
25551
|
+
speed: "normal",
|
|
25552
|
+
cost: "balanced",
|
|
25553
|
+
contextBudget: routing.requirements.contextBudget,
|
|
25554
|
+
qualityScore: null,
|
|
25555
|
+
isAvailable: true,
|
|
25556
|
+
requiresApiKey: false,
|
|
25557
|
+
metadata: {
|
|
25558
|
+
adapter: options.routingLocalAdapter,
|
|
25559
|
+
command: options.routingLocalCommand
|
|
25560
|
+
}
|
|
25561
|
+
};
|
|
25562
|
+
const resolution2 = normalizeAdaptiveRoutingResolution(
|
|
25563
|
+
runOrchestratorJsonCommand(
|
|
25564
|
+
[
|
|
25565
|
+
"route",
|
|
25566
|
+
"--dry-run",
|
|
25567
|
+
"--json",
|
|
25568
|
+
"--work-profile-json",
|
|
25569
|
+
JSON.stringify(routing.workProfile),
|
|
25570
|
+
"--requirements-json",
|
|
25571
|
+
JSON.stringify(routing.requirements),
|
|
25572
|
+
"--candidate-json",
|
|
25573
|
+
JSON.stringify(candidate)
|
|
25574
|
+
],
|
|
25575
|
+
cwd
|
|
25576
|
+
)
|
|
25577
|
+
);
|
|
25578
|
+
return {
|
|
25579
|
+
...routing,
|
|
25580
|
+
gateway: {
|
|
25581
|
+
source: "local_orchestrator",
|
|
25582
|
+
success: true,
|
|
25583
|
+
resolutionStatus: resolution2?.status ?? "resolved",
|
|
25584
|
+
candidateCount: 1,
|
|
25585
|
+
fallback: "main_agent",
|
|
25586
|
+
warnings: []
|
|
25587
|
+
},
|
|
25588
|
+
runtimeCatalog: {
|
|
25589
|
+
source: "declared_cli_worker",
|
|
25590
|
+
candidates: [candidate],
|
|
25591
|
+
workerEndpoints: {
|
|
25592
|
+
[candidateId]: {
|
|
25593
|
+
adapter: options.routingLocalAdapter,
|
|
25594
|
+
command: options.routingLocalCommand
|
|
25595
|
+
}
|
|
25596
|
+
}
|
|
25597
|
+
},
|
|
25598
|
+
resolution: resolution2
|
|
25599
|
+
};
|
|
25600
|
+
}
|
|
25458
25601
|
const catalogArgs = [
|
|
25459
25602
|
"local-model-catalog",
|
|
25460
25603
|
"--json",
|
|
@@ -25477,6 +25620,12 @@ function enrichAdaptiveRoutingWithLocalOrchestrator(routing, options, cwd = proc
|
|
|
25477
25620
|
if (options.routingLocalProvider) {
|
|
25478
25621
|
catalogArgs.push("--provider", options.routingLocalProvider);
|
|
25479
25622
|
}
|
|
25623
|
+
if (options.routingLocalApiKeyEnv) {
|
|
25624
|
+
catalogArgs.push("--api-key-env", options.routingLocalApiKeyEnv);
|
|
25625
|
+
}
|
|
25626
|
+
if (options.routingLocalApiKeyHeader) {
|
|
25627
|
+
catalogArgs.push("--api-key-header", options.routingLocalApiKeyHeader);
|
|
25628
|
+
}
|
|
25480
25629
|
const runtimeCatalog = normalizeAdaptiveRoutingCatalog(
|
|
25481
25630
|
runOrchestratorJsonCommand(catalogArgs, cwd)
|
|
25482
25631
|
);
|
|
@@ -27367,7 +27516,7 @@ async function workflowRunCommand(options) {
|
|
|
27367
27516
|
}
|
|
27368
27517
|
function shouldBuildAdaptiveRouting(options) {
|
|
27369
27518
|
return Boolean(
|
|
27370
|
-
options.adaptiveRoutingDryRun || options.routeLocalWorkers || options.routingLocalWorker || options.routingWorkerRole || options.routingPreferredEndpoints?.length || options.routingAllowedEndpoints?.length || options.routingLocalBaseUrl || options.routingLocalModel || options.routingLocalPreferModel || options.routingLocalProvider || options.plannerRetainsReasoning
|
|
27519
|
+
options.adaptiveRoutingDryRun || options.routeLocalWorkers || options.routingLocalWorker || options.routingWorkerRole || options.routingPreferredEndpoints?.length || options.routingAllowedEndpoints?.length || options.routingLocalBaseUrl || options.routingLocalModel || options.routingLocalPreferModel || options.routingLocalProvider || options.routingLocalApiKeyEnv || options.plannerRetainsReasoning || options.strongRepair
|
|
27371
27520
|
);
|
|
27372
27521
|
}
|
|
27373
27522
|
function buildWorkflowAdaptiveRouting(options, policy = null, intentWarnings = []) {
|
|
@@ -27411,6 +27560,7 @@ function buildWorkflowAdaptiveRouting(options, policy = null, intentWarnings = [
|
|
|
27411
27560
|
allowedEndpointTypes,
|
|
27412
27561
|
workerRole,
|
|
27413
27562
|
plannerRetainsReasoning: options.plannerRetainsReasoning ?? policy?.plannerRetainsReasoning ?? (options.routeLocalWorkers ? true : void 0),
|
|
27563
|
+
strongRepair: options.strongRepair,
|
|
27414
27564
|
catalogLimit: policy?.catalogLimit ?? DEFAULT_ADAPTIVE_ROUTING_CATALOG_LIMIT,
|
|
27415
27565
|
dailyBudgetCents: policy?.dailyBudgetCents,
|
|
27416
27566
|
monthlyBudgetCents: policy?.monthlyBudgetCents
|
|
@@ -38817,8 +38967,17 @@ workflow.command("run").description("Run a LITE/STANDARD/FULL/orchestrate workfl
|
|
|
38817
38967
|
"--routing-local-prefer-model <text>",
|
|
38818
38968
|
"Prefer a local /v1/models entry containing this text during worker routing"
|
|
38819
38969
|
).option("--routing-local-provider <provider>", "Provider label for local worker routing").option(
|
|
38970
|
+
"--routing-local-api-key-env <env>",
|
|
38971
|
+
"Environment variable containing the local/remote provider API key"
|
|
38972
|
+
).option(
|
|
38973
|
+
"--routing-local-api-key-header <header>",
|
|
38974
|
+
"Provider API key header: authorization or x-api-key"
|
|
38975
|
+
).option(
|
|
38820
38976
|
"--planner-retains-reasoning",
|
|
38821
38977
|
"Mark the main planner as retaining deep reasoning while the worker executes scoped work"
|
|
38978
|
+
).option(
|
|
38979
|
+
"--strong-repair",
|
|
38980
|
+
"Allow one bounded strong-adapter repair after local proof or output validation fails"
|
|
38822
38981
|
).option("--write-plan-file <file>", "Write the generated FULL-mode plan as workflow JSON").option(
|
|
38823
38982
|
"--start-workflow-from-plan",
|
|
38824
38983
|
"Start a local managed workflow from the generated FULL-mode plan"
|
|
@@ -38844,7 +39003,10 @@ workflow.command("run").description("Run a LITE/STANDARD/FULL/orchestrate workfl
|
|
|
38844
39003
|
routingLocalModel: options.routingLocalModel,
|
|
38845
39004
|
routingLocalPreferModel: options.routingLocalPreferModel,
|
|
38846
39005
|
routingLocalProvider: options.routingLocalProvider,
|
|
39006
|
+
routingLocalApiKeyEnv: options.routingLocalApiKeyEnv,
|
|
39007
|
+
routingLocalApiKeyHeader: options.routingLocalApiKeyHeader,
|
|
38847
39008
|
plannerRetainsReasoning: options.plannerRetainsReasoning ? true : void 0,
|
|
39009
|
+
strongRepair: Boolean(options.strongRepair),
|
|
38848
39010
|
writePlanFile: options.writePlanFile,
|
|
38849
39011
|
startWorkflowFromPlan: Boolean(options.startWorkflowFromPlan),
|
|
38850
39012
|
workflowId: options.workflowId,
|
|
@@ -38944,7 +39106,11 @@ localWorkers.command("add").description("Declare a local worker and enable local
|
|
|
38944
39106
|
"--role <role>",
|
|
38945
39107
|
"Worker role, for example coding, documentation, tests, or review",
|
|
38946
39108
|
"coding"
|
|
38947
|
-
).option("--provider <provider>", "Provider label", "lm-studio").option("--base-url <url>", "OpenAI-compatible local runtime base URL", "http://127.0.0.1:1234").option("--
|
|
39109
|
+
).option("--provider <provider>", "Provider label", "lm-studio").option("--base-url <url>", "OpenAI-compatible local runtime base URL", "http://127.0.0.1:1234").option("--api-key-env <env>", "Environment variable containing the provider API key").option(
|
|
39110
|
+
"--api-key-header <header>",
|
|
39111
|
+
"Provider API key header: authorization or x-api-key",
|
|
39112
|
+
"authorization"
|
|
39113
|
+
).option("--model <id>", "Exact model id exposed by the local runtime").option(
|
|
38948
39114
|
"--prefer-model <text>",
|
|
38949
39115
|
"Fallback model id substring to prefer when no exact model is set"
|
|
38950
39116
|
).option("--transport <openai_http|cli>", "Worker transport: openai_http (default) or cli").option("--command <command>", "CLI command to execute when transport is cli").option("--capability <capability>", "Worker capability; repeatable", collectOption, []).option(
|
|
@@ -38971,6 +39137,8 @@ localWorkers.command("add").description("Declare a local worker and enable local
|
|
|
38971
39137
|
preferModel: options.preferModel,
|
|
38972
39138
|
transport: options.transport,
|
|
38973
39139
|
command: options.command,
|
|
39140
|
+
apiKeyEnv: options.apiKeyEnv,
|
|
39141
|
+
apiKeyHeader: options.apiKeyHeader,
|
|
38974
39142
|
capabilities: options.capability,
|
|
38975
39143
|
reasoning: options.reasoning ? options.reasoning.toLowerCase() : void 0,
|
|
38976
39144
|
contextWindow: options.contextWindow,
|
|
@@ -38988,7 +39156,11 @@ localWorkers.command("list").description("List declared local workers").option("
|
|
|
38988
39156
|
localWorkers.command("remove").description("Remove a declared local worker").argument("id", "Local worker id").option("--json", "Print raw JSON").action((id, options) => {
|
|
38989
39157
|
workersLocalRemoveCommand({ id, json: options.json });
|
|
38990
39158
|
});
|
|
38991
|
-
localWorkers.command("probe").description("Probe an OpenAI-compatible endpoint and draft a worker suggestion").option("--base-url <url>", "OpenAI-compatible local runtime base URL", "http://127.0.0.1:1234").option("--
|
|
39159
|
+
localWorkers.command("probe").description("Probe an OpenAI-compatible endpoint and draft a worker suggestion").option("--base-url <url>", "OpenAI-compatible local runtime base URL", "http://127.0.0.1:1234").option("--api-key-env <env>", "Environment variable containing the provider API key").option(
|
|
39160
|
+
"--api-key-header <header>",
|
|
39161
|
+
"Provider API key header: authorization or x-api-key",
|
|
39162
|
+
"authorization"
|
|
39163
|
+
).option("--provider <provider>", "Provider label", "lm-studio").option("--model <id>", "Exact model id exposed by the local runtime").option("--prefer-model <text>", "Fallback local model substring when exact model is unset").option(
|
|
38992
39164
|
"--role <role>",
|
|
38993
39165
|
"Worker role; for example coding, documentation, tests, or review",
|
|
38994
39166
|
"coding"
|
|
@@ -39019,6 +39191,8 @@ localWorkers.command("probe").description("Probe an OpenAI-compatible endpoint a
|
|
|
39019
39191
|
reasoning: options.reasoning ? options.reasoning.toLowerCase() : void 0,
|
|
39020
39192
|
contextWindow: options.contextWindow,
|
|
39021
39193
|
writeScope: options.writeScope,
|
|
39194
|
+
apiKeyEnv: options.apiKeyEnv,
|
|
39195
|
+
apiKeyHeader: options.apiKeyHeader,
|
|
39022
39196
|
json: options.json
|
|
39023
39197
|
});
|
|
39024
39198
|
});
|
package/docs/FULL_REFERENCE.md
CHANGED
|
@@ -421,6 +421,7 @@ snipara-companion workflow run \
|
|
|
421
421
|
--routing-allowed-endpoint local \
|
|
422
422
|
--routing-allowed-endpoint cloud \
|
|
423
423
|
--planner-retains-reasoning \
|
|
424
|
+
--strong-repair \
|
|
424
425
|
"Update documentation for the new gateway"
|
|
425
426
|
```
|
|
426
427
|
|
|
@@ -464,7 +465,8 @@ snipara-companion workers local add \
|
|
|
464
465
|
--role coding \
|
|
465
466
|
--provider lm-studio \
|
|
466
467
|
--base-url http://127.0.0.1:1234 \
|
|
467
|
-
--model openai/gpt-oss-20b
|
|
468
|
+
--model openai/gpt-oss-20b \
|
|
469
|
+
--api-key-env LM_STUDIO_API_KEY
|
|
468
470
|
```
|
|
469
471
|
|
|
470
472
|
The declaration is written under `.snipara/workers/<worker-id>.json`; Companion
|
|
@@ -499,7 +501,11 @@ Do not store secrets in worker profiles. Local endpoints such as
|
|
|
499
501
|
`http://127.0.0.1:1234` are safe to commit when they contain no credentials, but
|
|
500
502
|
cloud CLI transports or authenticated endpoints must reference secrets through
|
|
501
503
|
environment variables rather than embedding API keys, bearer tokens, passwords,
|
|
502
|
-
or private URLs directly in `.snipara/workers/*.json`.
|
|
504
|
+
or private URLs directly in `.snipara/workers/*.json`. `--api-key-env` stores
|
|
505
|
+
only the variable name; pair it with `--api-key-header authorization` (default)
|
|
506
|
+
or `--api-key-header x-api-key`. Native Codex and Claude profiles use their
|
|
507
|
+
host-managed credentials. A generic `cli://command` profile fails closed unless
|
|
508
|
+
the command maps to a supported Codex or Claude adapter.
|
|
503
509
|
|
|
504
510
|
Use `workers local probe` to query a local endpoint and preview a declaration
|
|
505
511
|
proposal before committing it:
|
|
@@ -518,6 +524,12 @@ routing/handoff contract: Companion resolves the local candidate through
|
|
|
518
524
|
`snipara-orchestrator`, records metadata, and leaves execution plus proof review
|
|
519
525
|
to the supervising agent workflow.
|
|
520
526
|
|
|
527
|
+
Add `--strong-repair` when the supervising workflow should permit one strong
|
|
528
|
+
adapter repair after a local proof or output failure. The handoff records the
|
|
529
|
+
contract (`maxAttempts: 1`, same scope and proof, strong adapter as final
|
|
530
|
+
authority, `main_agent` fallback); Companion remains recommendation-only and
|
|
531
|
+
does not launch the worker.
|
|
532
|
+
|
|
521
533
|
Use Qwen for reflection, architecture, and documentation:
|
|
522
534
|
|
|
523
535
|
```bash
|
|
@@ -559,6 +571,39 @@ snipara-orchestrator local-model-catalog \
|
|
|
559
571
|
--json > .snipara/local-devstral-runtime-catalog.json
|
|
560
572
|
```
|
|
561
573
|
|
|
574
|
+
For a remote OpenAI-compatible runtime, use an explicit allowlist and an
|
|
575
|
+
environment-backed key when running the native host:
|
|
576
|
+
|
|
577
|
+
```bash
|
|
578
|
+
snipara-orchestrator host run \
|
|
579
|
+
--adapter openai_compatible \
|
|
580
|
+
--base-url https://provider.example.com --allow-remote \
|
|
581
|
+
--api-key-env PROVIDER_API_KEY --api-key-header authorization \
|
|
582
|
+
--model provider/coder --task "Bounded task" --workspace . \
|
|
583
|
+
--write-scope docs/** --proof "git diff --check" --execute
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
For a loopback local worker with one explicit strong repair attempt:
|
|
587
|
+
|
|
588
|
+
```bash
|
|
589
|
+
snipara-orchestrator host run \
|
|
590
|
+
--adapter openai_compatible --base-url http://127.0.0.1:1234 \
|
|
591
|
+
--model local/coder --task "Bounded task" --workspace . \
|
|
592
|
+
--write-scope docs/** --proof "git diff --check" \
|
|
593
|
+
--strong-repair --repair-adapter codex_app_server --execute
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
The repair reuses the approval envelope when approval is required, reruns the
|
|
597
|
+
same proof, and records redacted repair metrics in the durable host receipt.
|
|
598
|
+
Scope violations, unavailable repair hosts, skipped proof, or a failed repair
|
|
599
|
+
escalate without a retry loop.
|
|
600
|
+
|
|
601
|
+
Native host proof commands run automatically after dispatch. Use
|
|
602
|
+
`--no-run-proof` only when a reviewer will validate the receipt; the state then
|
|
603
|
+
remains `verification_required`. Use `--require-approval` with both an approval
|
|
604
|
+
receipt id and `--approval-receipt-file <json>` for unattended execution; an id
|
|
605
|
+
alone never bypasses the approval gate.
|
|
606
|
+
|
|
562
607
|
The local catalog records the OpenAI-compatible routes exposed by LM Studio:
|
|
563
608
|
`GET /v1/models`, `POST /v1/responses`, `POST /v1/chat/completions`,
|
|
564
609
|
`POST /v1/completions`, and `POST /v1/embeddings`. `--prefer-model devstral`
|