sentinelayer-cli 0.9.8 → 0.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sentinelayer-cli",
3
- "version": "0.9.8",
3
+ "version": "0.10.0",
4
4
  "description": "Scaffold Sentinelayer spec/prompt/guide artifacts with secure browser auth and token bootstrap.",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/ai/proxy.js CHANGED
@@ -27,7 +27,15 @@ const PROXY_RETRY_STATUSES = new Set([429, 502, 503, 504]);
27
27
  * @param {number} [options.temperature] - Temperature (default: 0.1)
28
28
  * @param {string} [options.apiUrl] - Override API URL
29
29
  * @param {string} [options.token] - Override Bearer token
30
- * @returns {Promise<{ text: string, usage: { inputTokens: number, outputTokens: number, costUsd: number, model: string, provider: string, latencyMs: number } }>}
30
+ * @param {string} [options.sessionId] - Optional Senti session id for server-side usage metering
31
+ * @param {string} [options.agentId] - Optional session agent id for server-side usage metering
32
+ * @param {string} [options.action] - Optional metered action, defaults server-side when omitted
33
+ * @param {string} [options.usageIdempotencyKey] - Stable per-intent key for proxy + ledger idempotency
34
+ * @param {string} [options.billingTier] - Optional billing tier hint
35
+ * @param {string} [options.customerPricingPolicy] - Optional customer pricing policy hint
36
+ * @param {object} [options.metadata] - Optional allowlisted billing metadata
37
+ * @param {Function} [options.fetchImpl] - Optional fetch implementation for tests
38
+ * @returns {Promise<{ text: string, usage: { inputTokens: number, outputTokens: number, costUsd: number, model: string, provider: string, latencyMs: number }, usageLedger: object | null }>}
31
39
  */
32
40
  export async function invokeViaProxy({
33
41
  prompt,
@@ -37,6 +45,14 @@ export async function invokeViaProxy({
37
45
  temperature = 0.1,
38
46
  apiUrl = "",
39
47
  token = "",
48
+ sessionId = "",
49
+ agentId = "",
50
+ action = "",
51
+ usageIdempotencyKey = "",
52
+ billingTier = "",
53
+ customerPricingPolicy = "",
54
+ metadata = null,
55
+ fetchImpl = fetch,
40
56
  } = {}) {
41
57
  // Resolve credentials from session if not provided
42
58
  let resolvedApiUrl = String(apiUrl || "").trim();
@@ -59,13 +75,40 @@ export async function invokeViaProxy({
59
75
 
60
76
  const url = `${resolvedApiUrl.replace(/\/+$/, "")}/api/v1/proxy/llm`;
61
77
 
62
- const body = JSON.stringify({
78
+ const requestBody = {
63
79
  model,
64
80
  system_prompt: systemPrompt || "You are a code reviewer.",
65
81
  user_content: String(prompt || ""),
66
82
  max_tokens: maxTokens,
67
83
  temperature,
68
- });
84
+ };
85
+
86
+ const normalizedSessionId = String(sessionId || "").trim();
87
+ const normalizedAgentId = String(agentId || "").trim();
88
+ const normalizedAction = String(action || "").trim();
89
+ const normalizedUsageIdempotencyKey = String(usageIdempotencyKey || "").trim();
90
+ const normalizedBillingTier = String(billingTier || "").trim();
91
+ const normalizedCustomerPricingPolicy = String(customerPricingPolicy || "").trim();
92
+ if (normalizedSessionId) requestBody.session_id = normalizedSessionId;
93
+ if (normalizedAgentId) requestBody.agent_id = normalizedAgentId;
94
+ if (normalizedAction) requestBody.action = normalizedAction;
95
+ if (normalizedUsageIdempotencyKey) requestBody.usage_idempotency_key = normalizedUsageIdempotencyKey;
96
+ if (normalizedBillingTier) requestBody.billing_tier = normalizedBillingTier;
97
+ if (normalizedCustomerPricingPolicy) requestBody.customer_pricing_policy = normalizedCustomerPricingPolicy;
98
+ if (metadata && typeof metadata === "object" && !Array.isArray(metadata)) {
99
+ requestBody.metadata = metadata;
100
+ }
101
+
102
+ const headers = {
103
+ "Content-Type": "application/json",
104
+ Authorization: `Bearer ${resolvedToken}`,
105
+ Accept: "application/json",
106
+ };
107
+ if (normalizedUsageIdempotencyKey) {
108
+ headers["Idempotency-Key"] = normalizedUsageIdempotencyKey;
109
+ }
110
+
111
+ const body = JSON.stringify(requestBody);
69
112
 
70
113
  let response = null;
71
114
  let lastError = null;
@@ -75,13 +118,9 @@ export async function invokeViaProxy({
75
118
  const controller = new AbortController();
76
119
  const timeoutHandle = setTimeout(() => controller.abort(), PROXY_TIMEOUT_MS);
77
120
  try {
78
- response = await fetch(url, {
121
+ response = await fetchImpl(url, {
79
122
  method: "POST",
80
- headers: {
81
- "Content-Type": "application/json",
82
- Authorization: `Bearer ${resolvedToken}`,
83
- Accept: "application/json",
84
- },
123
+ headers,
85
124
  body,
86
125
  signal: controller.signal,
87
126
  });
@@ -131,6 +170,7 @@ export async function invokeViaProxy({
131
170
  provider: result.usage?.provider || "sentinelayer",
132
171
  latencyMs: result.usage?.latency_ms || 0,
133
172
  },
173
+ usageLedger: result.usageLedger || result.usage_ledger || null,
134
174
  };
135
175
  }
136
176
 
@@ -453,6 +453,10 @@ async function buildHelpResponseMessage(
453
453
  normalizePositiveInteger(daemonState.helpRequestTimeoutMs, HELP_REQUEST_TIMEOUT_MS) * 2
454
454
  )
455
455
  );
456
+ const requestCorrelationId =
457
+ normalizeString(requestEvent?.payload?.requestId) ||
458
+ normalizeString(requestEvent?.requestId) ||
459
+ normalizeIsoTimestamp(requestEvent?.ts, daemonState.startedAt);
456
460
 
457
461
  try {
458
462
  const llmResult = await runWithTimeout(
@@ -463,6 +467,15 @@ async function buildHelpResponseMessage(
463
467
  prompt: userPrompt,
464
468
  maxTokens: 320,
465
469
  temperature: 0.1,
470
+ sessionId: daemonState.sessionId,
471
+ agentId: SENTI_IDENTITY.id,
472
+ action: "proxy_llm",
473
+ usageIdempotencyKey: `senti:${daemonState.sessionId}:help:${requestCorrelationId}`,
474
+ billingTier: "internal",
475
+ metadata: {
476
+ purpose: "senti_help_response",
477
+ runId: requestCorrelationId,
478
+ },
466
479
  })
467
480
  ),
468
481
  llmTimeoutMs,