snipara-companion 2.0.1 → 2.0.2

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
@@ -72,6 +72,12 @@ blocked by default; use `--allow-sensitive` only when you intentionally need an
72
72
  exact local recovery artifact, and prefer the exact pack ID over `latest` in
73
73
  handoffs.
74
74
 
75
+ Metadata-only context-pack receipts include token economy fields:
76
+ `baseline_tokens`, `packed_tokens`, `retrieved_tokens`, and `saved_tokens`.
77
+ Pack/reference receipts can claim saved tokens because only receipt metadata is
78
+ uploaded; retrieve receipts set retrieved tokens to the local baseline so they
79
+ do not overstate savings.
80
+
75
81
  ```mermaid
76
82
  flowchart LR
77
83
  Project["Local project"] --> Companion["snipara-companion"]
@@ -244,6 +250,15 @@ Project owners can configure Adaptive Work Routing in Project > Automation:
244
250
  `off`, `recommend`, or `catalog`; approval; planner-retained reasoning; allowed
245
251
  endpoint types (`cloud`, `local`, `self_hosted`); worker classes; and budget
246
252
  hints. `workflow run` reads that hosted policy and CLI flags cannot broaden it.
253
+ Companion passes policy budgets into provider-neutral model requirements; the
254
+ hosted gateway enforces project and provider daily/monthly budgets when receipt
255
+ history is available.
256
+
257
+ Approval is an MCP contract, not a dashboard-only UX path. When project policy
258
+ requires approval, a coding agent calls `snipara_adaptive_routing_approve` with
259
+ the routing card or handoff subject, approved write scopes, endpoint types, a
260
+ stable `idempotency_key`, and optional cost/expiry bounds. Companion dry-runs
261
+ surface the approval requirement but do not auto-approve or spawn workers.
247
262
  Project credentials stay server-side behind the hosted gateway; local endpoints
248
263
  such as Ollama, LM Studio, AnythingLLM, or other OpenAI-compatible servers must
249
264
  be reachable from the worker execution environment.
package/dist/index.d.ts CHANGED
@@ -160,6 +160,10 @@ interface LocalContextPackReceiptPayload {
160
160
  tags: string[];
161
161
  bytes: number;
162
162
  line_count: number;
163
+ baseline_tokens?: number;
164
+ packed_tokens?: number;
165
+ retrieved_tokens?: number;
166
+ saved_tokens?: number;
163
167
  payload_digest?: string;
164
168
  sensitive: boolean;
165
169
  created_at: string;
@@ -3017,6 +3021,8 @@ interface AdaptiveModelRequirements {
3017
3021
  preferredEndpointTypes?: string[];
3018
3022
  allowedEndpointTypes?: string[];
3019
3023
  catalogLimit?: number;
3024
+ dailyBudgetCents?: number;
3025
+ monthlyBudgetCents?: number;
3020
3026
  fallback: "main_agent";
3021
3027
  }
3022
3028
  interface AdaptiveRoutingCostEstimate {
@@ -3062,6 +3068,8 @@ interface AdaptiveWorkRoutingOptions {
3062
3068
  workerRole?: string;
3063
3069
  plannerRetainsReasoning?: boolean;
3064
3070
  catalogLimit?: number;
3071
+ dailyBudgetCents?: number;
3072
+ monthlyBudgetCents?: number;
3065
3073
  }
3066
3074
  interface OrchestratorHandoffArtifact {
3067
3075
  schemaVersion: "snipara.orchestrator.handoff.v1";
package/dist/index.js CHANGED
@@ -2683,9 +2683,19 @@ async function contextPackCleanCommand(options = {}) {
2683
2683
  function uniqueStrings2(values) {
2684
2684
  return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)));
2685
2685
  }
2686
+ function estimateTokensFromBytes(bytes) {
2687
+ if (!Number.isFinite(bytes) || bytes <= 0) {
2688
+ return 0;
2689
+ }
2690
+ return Math.ceil(bytes / 4);
2691
+ }
2692
+ function estimateReceiptTokens(value) {
2693
+ return estimateTokensFromBytes(Buffer.byteLength(JSON.stringify(value), "utf8"));
2694
+ }
2686
2695
  function buildLocalContextPackReceipt(record, options = {}) {
2687
2696
  const operation = options.operation ?? "reference";
2688
- return {
2697
+ const baselineTokens = estimateTokensFromBytes(record.bytes);
2698
+ const baseReceipt = {
2689
2699
  version: "snipara.context_pack.receipt.v1",
2690
2700
  receipt_id: `${record.id}:${operation}:${record.updatedAt}`,
2691
2701
  pack_id: record.id,
@@ -2708,6 +2718,16 @@ function buildLocalContextPackReceipt(record, options = {}) {
2708
2718
  manifest_relative_path: record.storage.manifestRelativePath
2709
2719
  }
2710
2720
  };
2721
+ const packedTokens = estimateReceiptTokens(baseReceipt);
2722
+ const retrievedTokens = operation === "retrieve" ? baselineTokens : 0;
2723
+ const savedTokens = Math.max(0, baselineTokens - packedTokens - retrievedTokens);
2724
+ return {
2725
+ ...baseReceipt,
2726
+ baseline_tokens: baselineTokens,
2727
+ packed_tokens: packedTokens,
2728
+ retrieved_tokens: retrievedTokens,
2729
+ saved_tokens: savedTokens
2730
+ };
2711
2731
  }
2712
2732
  function buildLocalContextPackReceipts(options) {
2713
2733
  return uniqueStrings2(options.ids).slice(0, 20).map((id) => {
@@ -11853,6 +11873,8 @@ function buildAdaptiveWorkRoutingRecommendation(options) {
11853
11873
  preferredEndpointTypes,
11854
11874
  allowedEndpointTypes,
11855
11875
  catalogLimit: options.catalogLimit,
11876
+ dailyBudgetCents: options.dailyBudgetCents,
11877
+ monthlyBudgetCents: options.monthlyBudgetCents,
11856
11878
  fallback: "main_agent"
11857
11879
  });
11858
11880
  const warnings = [
@@ -19673,7 +19695,9 @@ function buildWorkflowAdaptiveRouting(options, policy = null, intentWarnings = [
19673
19695
  allowedEndpointTypes,
19674
19696
  workerRole,
19675
19697
  plannerRetainsReasoning: options.plannerRetainsReasoning ?? policy?.plannerRetainsReasoning ?? (options.routeLocalWorkers ? true : void 0),
19676
- catalogLimit: policy?.catalogLimit ?? DEFAULT_ADAPTIVE_ROUTING_CATALOG_LIMIT
19698
+ catalogLimit: policy?.catalogLimit ?? DEFAULT_ADAPTIVE_ROUTING_CATALOG_LIMIT,
19699
+ dailyBudgetCents: policy?.dailyBudgetCents,
19700
+ monthlyBudgetCents: policy?.monthlyBudgetCents
19677
19701
  });
19678
19702
  const requestedWorkerRole = stringValue5(options.routingWorkerRole);
19679
19703
  let routing = buildRecommendation(requestedWorkerRole);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snipara-companion",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
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": {