snipara-companion 1.4.21 → 1.4.22

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/README.md CHANGED
@@ -127,6 +127,7 @@ snipara-companion brief --task "ship auth hardening" --changed-files src/auth.ts
127
127
  snipara-companion timeline
128
128
  snipara-companion workflow phase-commit build --summary "tests green"
129
129
  snipara-companion workflow impact-gate
130
+ snipara-companion workflow run --adaptive-routing-dry-run --route-local-workers "document a scoped change"
130
131
  snipara-companion verify --changed-files src/auth.ts --diff-summary "auth hardening"
131
132
  snipara-companion run --task "ship auth hardening" --changed-files src/auth.ts --release
132
133
  snipara-companion handoff --summary "status command shipped" --next "publish package"
@@ -141,6 +142,9 @@ snipara-companion workflow resume --include-session-context
141
142
  - `workflow impact-gate` audits committed local workflow phases that are ahead
142
143
  of upstream but not pushed. It does not push, and dirty working-tree files are
143
144
  reported separately from the committed diff.
145
+ - `workflow run --adaptive-routing-dry-run` prints an Adaptive Work Routing
146
+ card. Add `--route-local-workers` when a strong planner should keep deep
147
+ reasoning while a local worker handles scoped execution.
144
148
  - `verify` builds a transparent verification plan from companion code impact
145
149
  signals plus local package scripts. It recommends checks; it does not claim to
146
150
  execute them.
@@ -167,6 +171,35 @@ if the hosted call still times out. A hosted final-commit timeout does not modif
167
171
  Git state. Custom final-commit categories are namespaced under `final-commit`
168
172
  before the hosted call so they stay on the handoff-only path.
169
173
 
174
+ ## Adaptive Work Routing
175
+
176
+ Adaptive Work Routing is a recommendation-first path for routing scoped work to
177
+ the right worker class, endpoint type, and cost profile without hardcoding model
178
+ names in the CLI.
179
+
180
+ ```bash
181
+ snipara-companion workflow run \
182
+ --mode full \
183
+ --adaptive-routing-dry-run \
184
+ --route-local-workers \
185
+ --routing-worker-role coding \
186
+ --routing-preferred-endpoint local \
187
+ --routing-allowed-endpoint local \
188
+ --routing-allowed-endpoint cloud \
189
+ --planner-retains-reasoning \
190
+ "Update documentation for the new gateway"
191
+ ```
192
+
193
+ The output is a routing card and handoff metadata. Companion calls the hosted
194
+ `snipara_adaptive_routing_catalog` tool when project auth is configured, records
195
+ the sanitized runtime catalog in the handoff, and falls back to local dry-run
196
+ metadata when the hosted gateway is unavailable. It does not silently spawn
197
+ workers. The stable contract is provider-neutral requirements such as worker
198
+ role, reasoning depth, context budget, endpoint type, write scope, and fallback.
199
+ Project credentials stay server-side behind the hosted gateway; local endpoints
200
+ such as Ollama, LM Studio, AnythingLLM, or other OpenAI-compatible servers must
201
+ be reachable from the worker execution environment.
202
+
170
203
  ## Verification Plans
171
204
 
172
205
  Use `verify` when an agent asks what to prove before handoff or release:
package/dist/index.d.ts CHANGED
@@ -2837,10 +2837,24 @@ interface AdaptiveRoutingCard {
2837
2837
  reasons: string[];
2838
2838
  warnings: string[];
2839
2839
  }
2840
+ interface AdaptiveRoutingGatewayStatus {
2841
+ source: "hosted_mcp";
2842
+ success: boolean;
2843
+ resolutionStatus?: string;
2844
+ candidateCount: number;
2845
+ fallback?: string;
2846
+ warnings: string[];
2847
+ }
2848
+ interface AdaptiveRoutingRuntimeCatalog {
2849
+ version?: string;
2850
+ candidates: Array<Record<string, unknown>>;
2851
+ }
2840
2852
  interface AdaptiveWorkRoutingRecommendation {
2841
2853
  workProfile: AdaptiveWorkProfile;
2842
2854
  requirements: AdaptiveModelRequirements;
2843
2855
  routingCard: AdaptiveRoutingCard;
2856
+ gateway?: AdaptiveRoutingGatewayStatus;
2857
+ runtimeCatalog?: AdaptiveRoutingRuntimeCatalog;
2844
2858
  }
2845
2859
  interface AdaptiveWorkRoutingOptions {
2846
2860
  query: string;
@@ -2877,6 +2891,8 @@ interface OrchestratorHandoffArtifact {
2877
2891
  workProfile?: AdaptiveWorkProfile;
2878
2892
  requirements?: AdaptiveModelRequirements;
2879
2893
  routingCard?: AdaptiveRoutingCard;
2894
+ gateway?: AdaptiveRoutingGatewayStatus;
2895
+ runtimeCatalog?: AdaptiveRoutingRuntimeCatalog;
2880
2896
  };
2881
2897
  task: {
2882
2898
  title: string;
package/dist/index.js CHANGED
@@ -11144,7 +11144,9 @@ function buildOrchestratorHandoff(options) {
11144
11144
  ...options.adaptiveRouting ? {
11145
11145
  workProfile: options.adaptiveRouting.workProfile,
11146
11146
  requirements: options.adaptiveRouting.requirements,
11147
- routingCard: options.adaptiveRouting.routingCard
11147
+ routingCard: options.adaptiveRouting.routingCard,
11148
+ ...options.adaptiveRouting.gateway ? { gateway: options.adaptiveRouting.gateway } : {},
11149
+ ...options.adaptiveRouting.runtimeCatalog ? { runtimeCatalog: options.adaptiveRouting.runtimeCatalog } : {}
11148
11150
  } : {}
11149
11151
  },
11150
11152
  task: {
@@ -14648,6 +14650,20 @@ function stringValue5(value) {
14648
14650
  const stringified = String(value).trim();
14649
14651
  return stringified || void 0;
14650
14652
  }
14653
+ function numberValue5(value) {
14654
+ if (typeof value === "number" && Number.isFinite(value)) {
14655
+ return value;
14656
+ }
14657
+ const text = stringValue5(value);
14658
+ if (!text) {
14659
+ return void 0;
14660
+ }
14661
+ const parsed = Number(text);
14662
+ return Number.isFinite(parsed) ? parsed : void 0;
14663
+ }
14664
+ function uniqueStrings3(values) {
14665
+ return Array.from(new Set(values.filter((value) => value.trim().length > 0)));
14666
+ }
14651
14667
  function normalizeEnum(value) {
14652
14668
  return stringValue5(value)?.replace(/[-\s]/g, "_").toUpperCase() ?? "";
14653
14669
  }
@@ -17064,6 +17080,12 @@ function printAdaptiveRoutingRecommendation(routing) {
17064
17080
  if (routing.requirements.plannerRetainsReasoning) {
17065
17081
  printKeyValue("Planner retains reasoning:", "yes");
17066
17082
  }
17083
+ if (routing.gateway) {
17084
+ printKeyValue(
17085
+ "Hosted catalog:",
17086
+ routing.gateway.success ? `${routing.gateway.candidateCount} candidate(s), ${routing.gateway.resolutionStatus ?? "ready"}` : "unavailable"
17087
+ );
17088
+ }
17067
17089
  if (routing.routingCard.reasons.length > 0) {
17068
17090
  console.log("Reasons:");
17069
17091
  for (const reason of routing.routingCard.reasons) {
@@ -17077,6 +17099,73 @@ function printAdaptiveRoutingRecommendation(routing) {
17077
17099
  }
17078
17100
  }
17079
17101
  }
17102
+ async function enrichAdaptiveRoutingWithHostedCatalog(client, routing) {
17103
+ try {
17104
+ const result = await client.callTool(
17105
+ "snipara_adaptive_routing_catalog",
17106
+ {
17107
+ work_profile: routing.workProfile,
17108
+ model_requirements: routing.requirements,
17109
+ limit: 20
17110
+ }
17111
+ );
17112
+ const catalog = normalizeAdaptiveRoutingCatalog(result.catalog);
17113
+ const warnings = normalizeStringArray3(result.warnings) ?? [];
17114
+ const candidateCount = numberValue5(result.resolution?.candidate_count) ?? numberValue5(result.resolution?.candidateCount) ?? catalog.candidates.length;
17115
+ const gateway = {
17116
+ source: "hosted_mcp",
17117
+ success: result.success !== false,
17118
+ resolutionStatus: stringValue5(result.resolution?.status),
17119
+ candidateCount,
17120
+ fallback: stringValue5(result.fallback) ?? stringValue5(result.resolution?.fallback) ?? "main_agent",
17121
+ warnings
17122
+ };
17123
+ const reasons = [
17124
+ ...routing.routingCard.reasons,
17125
+ candidateCount > 0 ? `hosted adaptive routing catalog returned ${candidateCount} candidate(s)` : "hosted adaptive routing catalog returned no candidates and will fail closed"
17126
+ ];
17127
+ return {
17128
+ ...routing,
17129
+ gateway,
17130
+ runtimeCatalog: catalog,
17131
+ routingCard: {
17132
+ ...routing.routingCard,
17133
+ reasons: uniqueStrings3(reasons),
17134
+ warnings: uniqueStrings3([...routing.routingCard.warnings, ...warnings])
17135
+ }
17136
+ };
17137
+ } catch (error) {
17138
+ const warning = `Hosted adaptive routing catalog unavailable; keeping local dry-run metadata (${toPreview(error)}).`;
17139
+ return {
17140
+ ...routing,
17141
+ gateway: {
17142
+ source: "hosted_mcp",
17143
+ success: false,
17144
+ resolutionStatus: "unavailable",
17145
+ candidateCount: 0,
17146
+ fallback: "main_agent",
17147
+ warnings: [warning]
17148
+ },
17149
+ routingCard: {
17150
+ ...routing.routingCard,
17151
+ warnings: uniqueStrings3([...routing.routingCard.warnings, warning])
17152
+ }
17153
+ };
17154
+ }
17155
+ }
17156
+ function normalizeAdaptiveRoutingCatalog(value) {
17157
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
17158
+ return { candidates: [] };
17159
+ }
17160
+ const record = value;
17161
+ const candidates = Array.isArray(record.candidates) ? record.candidates.filter(
17162
+ (candidate) => Boolean(candidate) && typeof candidate === "object" && !Array.isArray(candidate)
17163
+ ) : [];
17164
+ return {
17165
+ version: stringValue5(record.version),
17166
+ candidates
17167
+ };
17168
+ }
17080
17169
  function printUploadResult(path20, result) {
17081
17170
  printKeyValue("Uploaded:", path20);
17082
17171
  printCompactObject(result, ["message", "document_id", "documentId", "version", "status"]);
@@ -18551,7 +18640,7 @@ async function workflowRunCommand(options) {
18551
18640
  ensureConfigured();
18552
18641
  const client = createClient(2e4);
18553
18642
  const adaptiveRoutingRequested = shouldBuildAdaptiveRouting(options);
18554
- const adaptiveRouting = adaptiveRoutingRequested ? buildWorkflowAdaptiveRouting(options) : null;
18643
+ const adaptiveRouting = adaptiveRoutingRequested ? await enrichAdaptiveRoutingWithHostedCatalog(client, buildWorkflowAdaptiveRouting(options)) : null;
18555
18644
  const orchestratorRecommendation = getOrchestratorRecommendation(options.query, options.mode, {
18556
18645
  policyAutoRoute: options.autoRouteOrchestrator,
18557
18646
  policySource: options.orchestratorPolicySource,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snipara-companion",
3
- "version": "1.4.21",
3
+ "version": "1.4.22",
4
4
  "description": "Snipara Git-style companion CLI for local workflow continuity, hooks, hosted context, and Mini Snipara OSS workflows",
5
5
  "main": "dist/index.js",
6
6
  "bin": {