snipara-companion 3.3.0 → 3.4.1

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 CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  Release notes for `snipara-companion`, newest first.
4
4
 
5
+ ## New In 3.4.1
6
+
7
+ - Declares provider API keys by environment-variable name and supports
8
+ `authorization` or `x-api-key` headers without persisting secret values.
9
+ - Resolves declared Codex and Claude CLI workers to explicit native adapters;
10
+ unsupported generic CLI profiles fail closed.
11
+ - Preserves opt-in local routing behavior while forwarding provider auth metadata
12
+ to the orchestrator catalog.
13
+
14
+ ## New In 3.4.0
15
+
16
+ - Adds repeatable `workers execute --output-fragment` contracts.
17
+ - Fails a worker receipt closed when declared output fragments are missing,
18
+ while preserving explicit approval, proof, and Git scope gates.
19
+ - Persists missing output fragments in the receipt for review and calibration.
20
+
5
21
  ## New In 3.3.0
6
22
 
7
23
  - Adds reviewed, scoped Worker Trust Promotion commands: `workers trust
package/dist/index.d.ts CHANGED
@@ -4478,6 +4478,8 @@ interface LocalWorkerDeclaration {
4478
4478
  endpointType: "local";
4479
4479
  provider: string;
4480
4480
  baseUrl: string;
4481
+ apiKeyEnv?: string;
4482
+ apiKeyHeader?: "authorization" | "x-api-key";
4481
4483
  command?: string;
4482
4484
  model?: string;
4483
4485
  preferModel?: string;
@@ -4496,11 +4498,16 @@ interface LocalWorkersConfig {
4496
4498
  interface LocalWorkerRoutingDefaults {
4497
4499
  worker: LocalWorkerDeclaration;
4498
4500
  routeLocalWorkers: true;
4501
+ routingLocalTransport: "openai_http" | "cli";
4502
+ routingLocalAdapter?: "codex_app_server" | "claude_cli";
4503
+ routingLocalCommand?: string;
4499
4504
  routingWorkerRole: string;
4500
4505
  routingLocalBaseUrl: string;
4501
4506
  routingLocalModel?: string;
4502
4507
  routingLocalPreferModel?: string;
4503
4508
  routingLocalProvider: string;
4509
+ routingLocalApiKeyEnv?: string;
4510
+ routingLocalApiKeyHeader?: "authorization" | "x-api-key";
4504
4511
  routingPreferredEndpoints: string[];
4505
4512
  routingAllowedEndpoints: string[];
4506
4513
  plannerRetainsReasoning: true;
@@ -4520,6 +4527,8 @@ interface LocalWorkerAddOptions {
4520
4527
  json?: boolean;
4521
4528
  transport?: "openai_http" | "cli";
4522
4529
  command?: string;
4530
+ apiKeyEnv?: string;
4531
+ apiKeyHeader?: "authorization" | "x-api-key";
4523
4532
  }
4524
4533
  interface LocalWorkerStatusOptions {
4525
4534
  json?: boolean;
@@ -4542,6 +4551,8 @@ interface LocalWorkerProbeOptions {
4542
4551
  writeScope?: string[];
4543
4552
  reasoning?: "low" | "medium" | "high";
4544
4553
  contextWindow?: number;
4554
+ apiKeyEnv?: string;
4555
+ apiKeyHeader?: "authorization" | "x-api-key";
4545
4556
  json?: boolean;
4546
4557
  }
4547
4558
  declare function workersLocalAddCommand(options: LocalWorkerAddOptions): void;
@@ -4575,6 +4586,7 @@ interface ControlledWorkerExecuteOptions {
4575
4586
  writeScope?: string[];
4576
4587
  acceptance?: string[];
4577
4588
  proof?: string[];
4589
+ outputFragments?: string[];
4578
4590
  workCategory?: WorkerTrustCategory;
4579
4591
  trustEvent?: string;
4580
4592
  profileHash?: string;
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;
@@ -11247,6 +11253,8 @@ function buildControlledWorkerExecutionReceipt(input) {
11247
11253
  const writeScope = uniqueStrings9(input.writeScope ?? []);
11248
11254
  const acceptanceCriteria = uniqueStrings9(input.acceptanceCriteria ?? []);
11249
11255
  const proofRequired = uniqueStrings9(input.proofRequired ?? []);
11256
+ const outputFragments = uniqueStrings9(input.outputFragments ?? []);
11257
+ const missingOutputFragments = uniqueStrings9(input.missingOutputFragments ?? []);
11250
11258
  const reasonCodes = /* @__PURE__ */ new Set([
11251
11259
  "controlled_worker_execution_v0",
11252
11260
  `controlled_worker_execution_${mode}`,
@@ -11266,6 +11274,8 @@ function buildControlledWorkerExecutionReceipt(input) {
11266
11274
  writeScope,
11267
11275
  acceptanceCriteria,
11268
11276
  proofRequired,
11277
+ outputFragments,
11278
+ missingOutputFragments,
11269
11279
  approvalReceiptId: input.approvalReceiptId ?? null,
11270
11280
  outcomeReceiptId: input.outcomeReceiptId ?? null,
11271
11281
  command,
@@ -11282,6 +11292,9 @@ function buildControlledWorkerExecutionReceipt(input) {
11282
11292
  if (acceptanceCriteria.length === 0) {
11283
11293
  reasonCodes.add("controlled_worker_execution_missing_acceptance");
11284
11294
  }
11295
+ if (missingOutputFragments.length > 0) {
11296
+ reasonCodes.add("controlled_worker_execution_output_contract_failed");
11297
+ }
11285
11298
  return {
11286
11299
  version: CONTROLLED_WORKER_EXECUTION_RECEIPT_VERSION,
11287
11300
  receiptId: `worker-exec-${receiptHash.slice(0, 16)}`,
@@ -11294,6 +11307,8 @@ function buildControlledWorkerExecutionReceipt(input) {
11294
11307
  writeScope,
11295
11308
  acceptanceCriteria,
11296
11309
  proofRequired,
11310
+ outputFragments,
11311
+ missingOutputFragments,
11297
11312
  approvalReceiptId: input.approvalReceiptId ?? null,
11298
11313
  outcomeReceiptId: input.outcomeReceiptId ?? null
11299
11314
  },
@@ -19618,6 +19633,9 @@ function workersLocalAddCommand(options) {
19618
19633
  printKeyValue("Role:", result.worker.workerRole);
19619
19634
  printKeyValue("Provider:", result.worker.provider);
19620
19635
  printKeyValue("Base URL:", result.worker.baseUrl);
19636
+ if (result.worker.apiKeyEnv) {
19637
+ printKeyValue("API key env:", result.worker.apiKeyEnv);
19638
+ }
19621
19639
  if (result.worker.model) {
19622
19640
  printKeyValue("Model:", result.worker.model);
19623
19641
  } else if (result.worker.preferModel) {
@@ -19709,7 +19727,9 @@ function workersLocalProbeCommand(options) {
19709
19727
  capabilities: normalizeStringList3(options.capabilities),
19710
19728
  writeScope: normalizeStringList3(options.writeScope),
19711
19729
  model,
19712
- preferModel
19730
+ preferModel,
19731
+ apiKeyEnv: options.apiKeyEnv,
19732
+ apiKeyHeader: options.apiKeyHeader
19713
19733
  });
19714
19734
  const catalogContextWindow = resolveContextWindowFromCatalog(catalog, model, preferModel);
19715
19735
  const contextWindow = normalizeContextWindow(
@@ -19728,7 +19748,9 @@ function workersLocalProbeCommand(options) {
19728
19748
  baseUrl,
19729
19749
  ...model ? { model } : {},
19730
19750
  ...preferModel ? { preferModel } : {},
19731
- provider
19751
+ provider,
19752
+ ...options.apiKeyEnv ? { apiKeyEnv: options.apiKeyEnv } : {},
19753
+ ...options.apiKeyHeader ? { apiKeyHeader: options.apiKeyHeader } : {}
19732
19754
  },
19733
19755
  capabilities: {
19734
19756
  roles: [role],
@@ -19882,7 +19904,26 @@ function resolveLocalWorkerRoutingDefaults(options = {}) {
19882
19904
  throw new Error(`Local worker ${requestedId} is not declared.`);
19883
19905
  }
19884
19906
  if (!worker.baseUrl || worker.baseUrl === "cli://command") {
19885
- return null;
19907
+ const command = worker.command?.toLowerCase() ?? "";
19908
+ const adapter = command.includes("codex") ? "codex_app_server" : command.includes("claude") ? "claude_cli" : void 0;
19909
+ if (!adapter) {
19910
+ throw new Error(
19911
+ `CLI worker ${worker.id} is not mapped to a supported native adapter; use a Codex or Claude command.`
19912
+ );
19913
+ }
19914
+ return {
19915
+ worker,
19916
+ routeLocalWorkers: true,
19917
+ routingLocalTransport: "cli",
19918
+ routingLocalBaseUrl: "cli://command",
19919
+ routingLocalAdapter: adapter,
19920
+ routingLocalCommand: worker.command,
19921
+ routingWorkerRole: worker.workerRole,
19922
+ routingLocalProvider: worker.provider,
19923
+ routingPreferredEndpoints: ["local"],
19924
+ routingAllowedEndpoints: ["local"],
19925
+ plannerRetainsReasoning: true
19926
+ };
19886
19927
  }
19887
19928
  if (!worker.model && !worker.preferModel) {
19888
19929
  const hasCatalogHint = true;
@@ -19891,11 +19932,14 @@ function resolveLocalWorkerRoutingDefaults(options = {}) {
19891
19932
  return {
19892
19933
  worker,
19893
19934
  routeLocalWorkers: true,
19935
+ routingLocalTransport: "openai_http",
19894
19936
  routingWorkerRole: worker.workerRole,
19895
19937
  routingLocalBaseUrl: worker.baseUrl,
19896
19938
  ...worker.model ? { routingLocalModel: worker.model } : {},
19897
19939
  ...worker.preferModel ? { routingLocalPreferModel: worker.preferModel } : {},
19898
19940
  routingLocalProvider: worker.provider,
19941
+ ...worker.apiKeyEnv ? { routingLocalApiKeyEnv: worker.apiKeyEnv } : {},
19942
+ ...worker.apiKeyHeader ? { routingLocalApiKeyHeader: worker.apiKeyHeader } : {},
19899
19943
  routingPreferredEndpoints: ["local"],
19900
19944
  routingAllowedEndpoints: ["local"],
19901
19945
  plannerRetainsReasoning: true
@@ -20003,7 +20047,9 @@ function normalizeWorkerProfileRecord(value) {
20003
20047
  transport: {
20004
20048
  ...transport,
20005
20049
  baseUrl,
20006
- provider: normalizeProvider(transport.provider ?? "lm-studio")
20050
+ provider: normalizeProvider(transport.provider ?? "lm-studio"),
20051
+ ...transport.apiKeyEnv ? { apiKeyEnv: transport.apiKeyEnv } : {},
20052
+ ...transport.apiKeyHeader ? { apiKeyHeader: transport.apiKeyHeader } : {}
20007
20053
  }
20008
20054
  };
20009
20055
  }
@@ -20038,7 +20084,9 @@ function toProfileFromLegacyWorker(worker) {
20038
20084
  baseUrl: worker.baseUrl,
20039
20085
  ...worker.model ? { model: worker.model } : {},
20040
20086
  ...worker.preferModel ? { preferModel: worker.preferModel } : {},
20041
- provider: worker.provider
20087
+ provider: worker.provider,
20088
+ ...worker.apiKeyEnv ? { apiKeyEnv: worker.apiKeyEnv } : {},
20089
+ ...worker.apiKeyHeader ? { apiKeyHeader: worker.apiKeyHeader } : {}
20042
20090
  },
20043
20091
  capabilities: {
20044
20092
  roles: [normalizeWorkerRole(worker.workerRole)],
@@ -20081,6 +20129,8 @@ function toLocalWorkerDeclaration(profile) {
20081
20129
  endpointType: "local",
20082
20130
  provider: normalizeProvider(profile.transport.provider ?? "lm-studio"),
20083
20131
  baseUrl: profile.transport.baseUrl,
20132
+ ...profile.transport.apiKeyEnv ? { apiKeyEnv: profile.transport.apiKeyEnv } : {},
20133
+ ...profile.transport.apiKeyHeader ? { apiKeyHeader: profile.transport.apiKeyHeader } : {},
20084
20134
  ...stringValue6(profile.transport.model) ? { model: stringValue6(profile.transport.model) } : {},
20085
20135
  ...stringValue6(profile.transport.preferModel) && !profile.transport.model ? { preferModel: stringValue6(profile.transport.preferModel) } : {},
20086
20136
  capabilities: normalizeWorkerCapabilities(profile.capabilities.roles),
@@ -20186,6 +20236,12 @@ function runOrchestratorLocalCatalog(options) {
20186
20236
  if (options.preferModel) {
20187
20237
  args.push("--prefer-model", options.preferModel);
20188
20238
  }
20239
+ if (options.apiKeyEnv) {
20240
+ args.push("--api-key-env", options.apiKeyEnv);
20241
+ }
20242
+ if (options.apiKeyHeader) {
20243
+ args.push("--api-key-header", options.apiKeyHeader);
20244
+ }
20189
20245
  try {
20190
20246
  const result = (0, import_node_child_process9.execFileSync)("snipara-orchestrator", args, {
20191
20247
  encoding: "utf8",
@@ -20321,12 +20377,15 @@ function normalizeWorkerTransport(options) {
20321
20377
  const baseUrl = normalizeBaseUrl(options.baseUrl ?? "http://127.0.0.1:1234");
20322
20378
  const model = stringValue6(options.model);
20323
20379
  const preferModel = stringValue6(options.preferModel);
20380
+ const apiKeyEnv = normalizeApiKeyEnvironmentName(options.apiKeyEnv);
20324
20381
  return {
20325
20382
  kind: "openai_http",
20326
20383
  baseUrl,
20327
20384
  ...model ? { model } : {},
20328
20385
  ...preferModel ? { preferModel } : {},
20329
- provider: normalizeProvider(stringValue6(options.provider) ?? "lm-studio")
20386
+ provider: normalizeProvider(stringValue6(options.provider) ?? "lm-studio"),
20387
+ ...apiKeyEnv ? { apiKeyEnv } : {},
20388
+ ...options.apiKeyHeader ? { apiKeyHeader: options.apiKeyHeader } : {}
20330
20389
  };
20331
20390
  }
20332
20391
  function normalizeProvider(value) {
@@ -20348,6 +20407,8 @@ function normalizeLegacyWorkerDeclaration(value) {
20348
20407
  endpointType: "local",
20349
20408
  provider: normalizeProvider(stringValue6(value.provider) ?? "lm-studio"),
20350
20409
  baseUrl,
20410
+ ...stringValue6(value.apiKeyEnv) ? { apiKeyEnv: stringValue6(value.apiKeyEnv) } : {},
20411
+ ...value.apiKeyHeader === "authorization" || value.apiKeyHeader === "x-api-key" ? { apiKeyHeader: value.apiKeyHeader } : {},
20351
20412
  ...stringValue6(value.model) ? { model: stringValue6(value.model) } : {},
20352
20413
  ...stringValue6(value.preferModel) ? { preferModel: stringValue6(value.preferModel) } : {},
20353
20414
  capabilities: normalizeStringList3(value.capabilities).length > 0 ? normalizeStringList3(value.capabilities) : defaultCapabilitiesForWorker(normalizeWorkerRole(value.workerRole)),
@@ -20425,6 +20486,16 @@ function stringValue6(value) {
20425
20486
  const stringified = String(value).trim();
20426
20487
  return stringified || void 0;
20427
20488
  }
20489
+ function normalizeApiKeyEnvironmentName(value) {
20490
+ const normalized = stringValue6(value);
20491
+ if (!normalized) {
20492
+ return void 0;
20493
+ }
20494
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(normalized)) {
20495
+ throw new Error("API key env must be a shell-safe environment variable name.");
20496
+ }
20497
+ return normalized;
20498
+ }
20428
20499
  function isRecord10(value) {
20429
20500
  return typeof value === "object" && value !== null && !Array.isArray(value);
20430
20501
  }
@@ -25416,7 +25487,7 @@ function shouldResolveAdaptiveRoutingLocally(options, routing) {
25416
25487
  return preferred.includes("local") || allowed.length === 0 || allowed.includes("local");
25417
25488
  }
25418
25489
  function hasLocalRoutingRequest(options) {
25419
- 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");
25490
+ 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");
25420
25491
  }
25421
25492
  function applyLocalWorkerRoutingDefaults(options, defaults) {
25422
25493
  if (!defaults) {
@@ -25425,6 +25496,9 @@ function applyLocalWorkerRoutingDefaults(options, defaults) {
25425
25496
  return {
25426
25497
  ...options,
25427
25498
  routeLocalWorkers: options.routeLocalWorkers ?? defaults.routeLocalWorkers,
25499
+ routingLocalTransport: options.routingLocalTransport ?? defaults.routingLocalTransport,
25500
+ routingLocalAdapter: options.routingLocalAdapter ?? defaults.routingLocalAdapter,
25501
+ routingLocalCommand: options.routingLocalCommand ?? defaults.routingLocalCommand,
25428
25502
  routingWorkerRole: options.routingWorkerRole ?? defaults.routingWorkerRole,
25429
25503
  routingPreferredEndpoints: options.routingPreferredEndpoints && options.routingPreferredEndpoints.length > 0 ? options.routingPreferredEndpoints : defaults.routingPreferredEndpoints,
25430
25504
  routingAllowedEndpoints: options.routingAllowedEndpoints && options.routingAllowedEndpoints.length > 0 ? options.routingAllowedEndpoints : defaults.routingAllowedEndpoints,
@@ -25432,6 +25506,8 @@ function applyLocalWorkerRoutingDefaults(options, defaults) {
25432
25506
  routingLocalModel: options.routingLocalModel ?? defaults.routingLocalModel,
25433
25507
  routingLocalPreferModel: options.routingLocalPreferModel ?? defaults.routingLocalPreferModel,
25434
25508
  routingLocalProvider: options.routingLocalProvider ?? defaults.routingLocalProvider,
25509
+ routingLocalApiKeyEnv: options.routingLocalApiKeyEnv ?? defaults.routingLocalApiKeyEnv,
25510
+ routingLocalApiKeyHeader: options.routingLocalApiKeyHeader ?? defaults.routingLocalApiKeyHeader,
25435
25511
  plannerRetainsReasoning: options.plannerRetainsReasoning ?? defaults.plannerRetainsReasoning
25436
25512
  };
25437
25513
  }
@@ -25446,6 +25522,67 @@ function runOrchestratorJsonCommand(args, cwd = process.cwd()) {
25446
25522
  }
25447
25523
  function enrichAdaptiveRoutingWithLocalOrchestrator(routing, options, cwd = process.cwd()) {
25448
25524
  try {
25525
+ if (options.routingLocalTransport === "cli") {
25526
+ const candidateId = options.routingLocalAdapter ?? options.routingLocalCommand ?? "declared-cli";
25527
+ const candidate = {
25528
+ candidateId,
25529
+ workerClass: routing.requirements.workerRole,
25530
+ catalogSource: "declared_cli_worker",
25531
+ endpointType: "local",
25532
+ workerRoles: [routing.requirements.workerRole],
25533
+ capabilities: routing.requirements.capabilities ?? [],
25534
+ writeScope: routing.requirements.writeScope ?? [],
25535
+ reasoning: routing.requirements.reasoning,
25536
+ speed: "normal",
25537
+ cost: "balanced",
25538
+ contextBudget: routing.requirements.contextBudget,
25539
+ qualityScore: null,
25540
+ isAvailable: true,
25541
+ requiresApiKey: false,
25542
+ metadata: {
25543
+ adapter: options.routingLocalAdapter,
25544
+ command: options.routingLocalCommand
25545
+ }
25546
+ };
25547
+ const resolution2 = normalizeAdaptiveRoutingResolution(
25548
+ runOrchestratorJsonCommand(
25549
+ [
25550
+ "route",
25551
+ "--dry-run",
25552
+ "--json",
25553
+ "--work-profile-json",
25554
+ JSON.stringify(routing.workProfile),
25555
+ "--requirements-json",
25556
+ JSON.stringify(routing.requirements),
25557
+ "--candidate-json",
25558
+ JSON.stringify(candidate)
25559
+ ],
25560
+ cwd
25561
+ )
25562
+ );
25563
+ return {
25564
+ ...routing,
25565
+ gateway: {
25566
+ source: "local_orchestrator",
25567
+ success: true,
25568
+ resolutionStatus: resolution2?.status ?? "resolved",
25569
+ candidateCount: 1,
25570
+ fallback: "main_agent",
25571
+ warnings: []
25572
+ },
25573
+ runtimeCatalog: {
25574
+ source: "declared_cli_worker",
25575
+ candidates: [candidate],
25576
+ workerEndpoints: {
25577
+ [candidateId]: {
25578
+ adapter: options.routingLocalAdapter,
25579
+ command: options.routingLocalCommand
25580
+ }
25581
+ }
25582
+ },
25583
+ resolution: resolution2
25584
+ };
25585
+ }
25449
25586
  const catalogArgs = [
25450
25587
  "local-model-catalog",
25451
25588
  "--json",
@@ -25468,6 +25605,12 @@ function enrichAdaptiveRoutingWithLocalOrchestrator(routing, options, cwd = proc
25468
25605
  if (options.routingLocalProvider) {
25469
25606
  catalogArgs.push("--provider", options.routingLocalProvider);
25470
25607
  }
25608
+ if (options.routingLocalApiKeyEnv) {
25609
+ catalogArgs.push("--api-key-env", options.routingLocalApiKeyEnv);
25610
+ }
25611
+ if (options.routingLocalApiKeyHeader) {
25612
+ catalogArgs.push("--api-key-header", options.routingLocalApiKeyHeader);
25613
+ }
25471
25614
  const runtimeCatalog = normalizeAdaptiveRoutingCatalog(
25472
25615
  runOrchestratorJsonCommand(catalogArgs, cwd)
25473
25616
  );
@@ -27358,7 +27501,7 @@ async function workflowRunCommand(options) {
27358
27501
  }
27359
27502
  function shouldBuildAdaptiveRouting(options) {
27360
27503
  return Boolean(
27361
- options.adaptiveRoutingDryRun || options.routeLocalWorkers || options.routingLocalWorker || options.routingWorkerRole || options.routingPreferredEndpoints?.length || options.routingAllowedEndpoints?.length || options.routingLocalBaseUrl || options.routingLocalModel || options.routingLocalPreferModel || options.routingLocalProvider || options.plannerRetainsReasoning
27504
+ 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
27362
27505
  );
27363
27506
  }
27364
27507
  function buildWorkflowAdaptiveRouting(options, policy = null, intentWarnings = []) {
@@ -29914,6 +30057,7 @@ function controlledWorkerExecuteCommand(options) {
29914
30057
  const execute = Boolean(options.execute);
29915
30058
  const mode = normalizeMode(options.mode, execute);
29916
30059
  const commandArgs = normalizeCommandArgs(options.commandArgs);
30060
+ const outputFragments = normalizeOutputFragments(options.outputFragments);
29917
30061
  const legacyCommand = stringValue8(options.command);
29918
30062
  const command = commandArgs.length > 0 ? commandArgs.join(" ") : legacyCommand;
29919
30063
  const risk = command ? commandRisk(command) : "none";
@@ -29959,6 +30103,7 @@ function controlledWorkerExecuteCommand(options) {
29959
30103
  let executionAttempted = false;
29960
30104
  let changedFiles = [];
29961
30105
  let scopeViolations = [];
30106
+ let missingOutputFragments = [];
29962
30107
  if (execute) reasonCodes.add("controlled_worker_execution_requested");
29963
30108
  if (!execute) reasonCodes.add("controlled_worker_execution_dry_run_only");
29964
30109
  if (missingApproval) reasonCodes.add("controlled_worker_execution_missing_approval");
@@ -29996,6 +30141,13 @@ function controlledWorkerExecuteCommand(options) {
29996
30141
  exitCode = typeof result2.status === "number" ? result2.status : 1;
29997
30142
  stdout = result2.stdout ?? "";
29998
30143
  stderr = result2.stderr ?? "";
30144
+ missingOutputFragments = outputFragments.filter(
30145
+ (fragment) => !normalizeComparableText(stdout).includes(normalizeComparableText(fragment))
30146
+ );
30147
+ if (missingOutputFragments.length > 0) {
30148
+ blocked = true;
30149
+ reasonCodes.add("controlled_worker_execution_output_contract_failed");
30150
+ }
29999
30151
  reasonCodes.add(
30000
30152
  exitCode === 0 ? "controlled_worker_execution_command_succeeded" : "controlled_worker_execution_command_failed"
30001
30153
  );
@@ -30030,6 +30182,8 @@ function controlledWorkerExecuteCommand(options) {
30030
30182
  writeScope: options.writeScope,
30031
30183
  acceptanceCriteria: options.acceptance,
30032
30184
  proofRequired: options.proof,
30185
+ outputFragments,
30186
+ missingOutputFragments,
30033
30187
  approvalReceiptId: options.approvalReceipt ?? null,
30034
30188
  outcomeReceiptId: options.outcomeReceipt ?? null,
30035
30189
  command: command ?? null,
@@ -30100,6 +30254,12 @@ function controlledWorkerExecuteCommand(options) {
30100
30254
  function normalizeCommandArgs(values) {
30101
30255
  return (values ?? []).map((value) => value.trim()).filter(Boolean);
30102
30256
  }
30257
+ function normalizeOutputFragments(values) {
30258
+ return [...new Set((values ?? []).map((value) => value.trim()).filter(Boolean))].slice(0, 32);
30259
+ }
30260
+ function normalizeComparableText(value) {
30261
+ return value.toLowerCase().replace(/\s+/g, "");
30262
+ }
30103
30263
  function gitScopeSnapshot(workspaceRoot2) {
30104
30264
  const status = (0, import_node_child_process12.spawnSync)("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
30105
30265
  cwd: workspaceRoot2,
@@ -38791,6 +38951,12 @@ workflow.command("run").description("Run a LITE/STANDARD/FULL/orchestrate workfl
38791
38951
  "--routing-local-prefer-model <text>",
38792
38952
  "Prefer a local /v1/models entry containing this text during worker routing"
38793
38953
  ).option("--routing-local-provider <provider>", "Provider label for local worker routing").option(
38954
+ "--routing-local-api-key-env <env>",
38955
+ "Environment variable containing the local/remote provider API key"
38956
+ ).option(
38957
+ "--routing-local-api-key-header <header>",
38958
+ "Provider API key header: authorization or x-api-key"
38959
+ ).option(
38794
38960
  "--planner-retains-reasoning",
38795
38961
  "Mark the main planner as retaining deep reasoning while the worker executes scoped work"
38796
38962
  ).option("--write-plan-file <file>", "Write the generated FULL-mode plan as workflow JSON").option(
@@ -38818,6 +38984,8 @@ workflow.command("run").description("Run a LITE/STANDARD/FULL/orchestrate workfl
38818
38984
  routingLocalModel: options.routingLocalModel,
38819
38985
  routingLocalPreferModel: options.routingLocalPreferModel,
38820
38986
  routingLocalProvider: options.routingLocalProvider,
38987
+ routingLocalApiKeyEnv: options.routingLocalApiKeyEnv,
38988
+ routingLocalApiKeyHeader: options.routingLocalApiKeyHeader,
38821
38989
  plannerRetainsReasoning: options.plannerRetainsReasoning ? true : void 0,
38822
38990
  writePlanFile: options.writePlanFile,
38823
38991
  startWorkflowFromPlan: Boolean(options.startWorkflowFromPlan),
@@ -38841,6 +39009,11 @@ workers.command("execute").description("Create a policy-gated Controlled Worker
38841
39009
  "Required proof or verification command; repeatable",
38842
39010
  collectOption,
38843
39011
  []
39012
+ ).option(
39013
+ "--output-fragment <fragment>",
39014
+ "Required output fragment; repeatable and checked after execution",
39015
+ collectOption,
39016
+ []
38844
39017
  ).option(
38845
39018
  "--work-category <category>",
38846
39019
  "Trust category; conservative task and scope signals can only escalate it"
@@ -38862,6 +39035,7 @@ workers.command("execute").description("Create a policy-gated Controlled Worker
38862
39035
  writeScope: options.writeScope,
38863
39036
  acceptance: options.acceptance,
38864
39037
  proof: options.proof,
39038
+ outputFragments: options.outputFragment,
38865
39039
  workCategory: options.workCategory,
38866
39040
  trustEvent: options.trustEvent,
38867
39041
  profileHash: options.profileHash,
@@ -38912,7 +39086,11 @@ localWorkers.command("add").description("Declare a local worker and enable local
38912
39086
  "--role <role>",
38913
39087
  "Worker role, for example coding, documentation, tests, or review",
38914
39088
  "coding"
38915
- ).option("--provider <provider>", "Provider label", "lm-studio").option("--base-url <url>", "OpenAI-compatible local runtime base URL", "http://127.0.0.1:1234").option("--model <id>", "Exact model id exposed by the local runtime").option(
39089
+ ).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(
39090
+ "--api-key-header <header>",
39091
+ "Provider API key header: authorization or x-api-key",
39092
+ "authorization"
39093
+ ).option("--model <id>", "Exact model id exposed by the local runtime").option(
38916
39094
  "--prefer-model <text>",
38917
39095
  "Fallback model id substring to prefer when no exact model is set"
38918
39096
  ).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(
@@ -38939,6 +39117,8 @@ localWorkers.command("add").description("Declare a local worker and enable local
38939
39117
  preferModel: options.preferModel,
38940
39118
  transport: options.transport,
38941
39119
  command: options.command,
39120
+ apiKeyEnv: options.apiKeyEnv,
39121
+ apiKeyHeader: options.apiKeyHeader,
38942
39122
  capabilities: options.capability,
38943
39123
  reasoning: options.reasoning ? options.reasoning.toLowerCase() : void 0,
38944
39124
  contextWindow: options.contextWindow,
@@ -38956,7 +39136,11 @@ localWorkers.command("list").description("List declared local workers").option("
38956
39136
  localWorkers.command("remove").description("Remove a declared local worker").argument("id", "Local worker id").option("--json", "Print raw JSON").action((id, options) => {
38957
39137
  workersLocalRemoveCommand({ id, json: options.json });
38958
39138
  });
38959
- 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("--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(
39139
+ 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(
39140
+ "--api-key-header <header>",
39141
+ "Provider API key header: authorization or x-api-key",
39142
+ "authorization"
39143
+ ).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(
38960
39144
  "--role <role>",
38961
39145
  "Worker role; for example coding, documentation, tests, or review",
38962
39146
  "coding"
@@ -38987,6 +39171,8 @@ localWorkers.command("probe").description("Probe an OpenAI-compatible endpoint a
38987
39171
  reasoning: options.reasoning ? options.reasoning.toLowerCase() : void 0,
38988
39172
  contextWindow: options.contextWindow,
38989
39173
  writeScope: options.writeScope,
39174
+ apiKeyEnv: options.apiKeyEnv,
39175
+ apiKeyHeader: options.apiKeyHeader,
38990
39176
  json: options.json
38991
39177
  });
38992
39178
  });
@@ -464,7 +464,8 @@ snipara-companion workers local add \
464
464
  --role coding \
465
465
  --provider lm-studio \
466
466
  --base-url http://127.0.0.1:1234 \
467
- --model openai/gpt-oss-20b
467
+ --model openai/gpt-oss-20b \
468
+ --api-key-env LM_STUDIO_API_KEY
468
469
  ```
469
470
 
470
471
  The declaration is written under `.snipara/workers/<worker-id>.json`; Companion
@@ -499,7 +500,11 @@ Do not store secrets in worker profiles. Local endpoints such as
499
500
  `http://127.0.0.1:1234` are safe to commit when they contain no credentials, but
500
501
  cloud CLI transports or authenticated endpoints must reference secrets through
501
502
  environment variables rather than embedding API keys, bearer tokens, passwords,
502
- or private URLs directly in `.snipara/workers/*.json`.
503
+ or private URLs directly in `.snipara/workers/*.json`. `--api-key-env` stores
504
+ only the variable name; pair it with `--api-key-header authorization` (default)
505
+ or `--api-key-header x-api-key`. Native Codex and Claude profiles use their
506
+ host-managed credentials. A generic `cli://command` profile fails closed unless
507
+ the command maps to a supported Codex or Claude adapter.
503
508
 
504
509
  Use `workers local probe` to query a local endpoint and preview a declaration
505
510
  proposal before committing it:
@@ -559,6 +564,24 @@ snipara-orchestrator local-model-catalog \
559
564
  --json > .snipara/local-devstral-runtime-catalog.json
560
565
  ```
561
566
 
567
+ For a remote OpenAI-compatible runtime, use an explicit allowlist and an
568
+ environment-backed key when running the native host:
569
+
570
+ ```bash
571
+ snipara-orchestrator host run \
572
+ --adapter openai_compatible \
573
+ --base-url https://provider.example.com --allow-remote \
574
+ --api-key-env PROVIDER_API_KEY --api-key-header authorization \
575
+ --model provider/coder --task "Bounded task" --workspace . \
576
+ --write-scope docs/** --proof "git diff --check" --execute
577
+ ```
578
+
579
+ Native host proof commands run automatically after dispatch. Use
580
+ `--no-run-proof` only when a reviewer will validate the receipt; the state then
581
+ remains `verification_required`. Use `--require-approval` with both an approval
582
+ receipt id and `--approval-receipt-file <json>` for unattended execution; an id
583
+ alone never bypasses the approval gate.
584
+
562
585
  The local catalog records the OpenAI-compatible routes exposed by LM Studio:
563
586
  `GET /v1/models`, `POST /v1/responses`, `POST /v1/chat/completions`,
564
587
  `POST /v1/completions`, and `POST /v1/embeddings`. `--prefer-model devstral`
@@ -1335,6 +1358,7 @@ snipara-companion workers execute \
1335
1358
  --write-scope docs/features/PROJECT_INTELLIGENCE.md \
1336
1359
  --acceptance "docs match shipped behavior" \
1337
1360
  --proof "pnpm --filter @snipara/web type-check" \
1361
+ --output-fragment "expected output line" \
1338
1362
  --project-id proj_123 \
1339
1363
  --json
1340
1364
  ```
@@ -1345,6 +1369,8 @@ values for real execution. A legacy `--command` string still requires a fresh
1345
1369
  approval receipt and cannot consume delegated trust. High-risk commands are
1346
1370
  blocked locally, and successful low-risk commands produce
1347
1371
  `verification_required` receipts so proof review remains explicit. When
1372
+ `--output-fragment` is provided, every declared fragment must appear in stdout;
1373
+ missing fragments fail the receipt closed and are listed in the contract.
1348
1374
  `--project-id` is provided,
1349
1375
  Companion also writes a local Unified Receipt Ledger projection under
1350
1376
  `.snipara/unified-receipts/`; use `--unified-output <file>` to choose the sidecar
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snipara-companion",
3
- "version": "3.3.0",
3
+ "version": "3.4.1",
4
4
  "description": "Local-first CLI that asks your repo what breaks before an AI coding agent edits it.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {