paisa-mcp 0.2.0 → 0.3.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/dist/index.js CHANGED
@@ -53145,6 +53145,90 @@ class PaisaApiClient {
53145
53145
  }
53146
53146
  }
53147
53147
 
53148
+ // ../../node_modules/.bun/@nimit9+signet-lib@0.1.15+d40099021b198cb0/node_modules/@nimit9/signet-lib/dist/crypto/index.js
53149
+ var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
53150
+ async function decryptFields(input2, paths, { decrypt, isCiphertext }) {
53151
+ const undecrypted = [];
53152
+ const pending = [];
53153
+ const looksEncrypted = (value, path) => {
53154
+ if (!isCiphertext)
53155
+ return true;
53156
+ try {
53157
+ return isCiphertext(value, path) !== false;
53158
+ } catch {
53159
+ return true;
53160
+ }
53161
+ };
53162
+ const decryptInto = (target, key, value, path) => {
53163
+ pending.push((async () => {
53164
+ try {
53165
+ const plain = await decrypt(value, path);
53166
+ if (typeof plain !== "string")
53167
+ throw new TypeError("decrypt returned a non-string");
53168
+ target[key] = plain;
53169
+ } catch {
53170
+ target[key] = null;
53171
+ undecrypted.push(path);
53172
+ }
53173
+ })());
53174
+ };
53175
+ const copies = new Map;
53176
+ const mine = new WeakSet;
53177
+ const copyOf = (v) => {
53178
+ if (mine.has(v))
53179
+ return v;
53180
+ let c = copies.get(v);
53181
+ if (!c) {
53182
+ c = Array.isArray(v) ? [...v] : { ...v };
53183
+ copies.set(v, c);
53184
+ mine.add(c);
53185
+ }
53186
+ return c;
53187
+ };
53188
+ const walk = (node2, keys, at, set2) => {
53189
+ if (Array.isArray(node2)) {
53190
+ const arr = copyOf(node2);
53191
+ set2(arr);
53192
+ arr.forEach((item, i) => {
53193
+ walk(item, keys, at ? `${at}.${i}` : String(i), (v) => {
53194
+ arr[i] = v;
53195
+ });
53196
+ });
53197
+ return;
53198
+ }
53199
+ if (!isObj(node2) || keys.length === 0)
53200
+ return;
53201
+ const [key, ...rest] = keys;
53202
+ if (!Object.hasOwn(node2, key))
53203
+ return;
53204
+ const obj = copyOf(node2);
53205
+ set2(obj);
53206
+ const path = at ? `${at}.${key}` : key;
53207
+ const value = obj[key];
53208
+ if (rest.length > 0) {
53209
+ walk(value, rest, path, (v) => {
53210
+ obj[key] = v;
53211
+ });
53212
+ return;
53213
+ }
53214
+ if (typeof value === "string" && value !== "" && looksEncrypted(value, path)) {
53215
+ decryptInto(obj, key, value, path);
53216
+ }
53217
+ };
53218
+ let data = input2;
53219
+ for (const p of paths) {
53220
+ const keys = p.split(".").filter(Boolean);
53221
+ if (keys.length === 0)
53222
+ continue;
53223
+ walk(data, keys, "", (v) => {
53224
+ data = v;
53225
+ });
53226
+ }
53227
+ await Promise.all(pending);
53228
+ undecrypted.sort();
53229
+ return { data, undecrypted };
53230
+ }
53231
+
53148
53232
  // src/crypto.ts
53149
53233
  var import_hash_wasm = __toESM(require_index_umd(), 1);
53150
53234
  var ENCRYPTED_TXN_FIELDS = ["description", "referenceNumber", "notes"];
@@ -53207,16 +53291,6 @@ async function decryptField(encrypted, key) {
53207
53291
  const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext);
53208
53292
  return new TextDecoder().decode(plaintext);
53209
53293
  }
53210
- async function decryptObjectFields(obj, fields, key) {
53211
- for (const field of fields) {
53212
- const val = obj[field];
53213
- if (typeof val !== "string" || !val)
53214
- continue;
53215
- try {
53216
- obj[field] = await decryptField(val, key);
53217
- } catch {}
53218
- }
53219
- }
53220
53294
  async function encryptObjectFields(obj, fields, key) {
53221
53295
  for (const field of fields) {
53222
53296
  const val = obj[field];
@@ -53225,25 +53299,55 @@ async function encryptObjectFields(obj, fields, key) {
53225
53299
  obj[field] = await encryptField(val, key);
53226
53300
  }
53227
53301
  }
53302
+ var CIPHERTEXT_CHARSET_RE = /^[A-Za-z0-9+/]+={0,2}$/;
53303
+ var MIN_CIPHERTEXT_B64_LEN = 40;
53304
+ function looksLikeCiphertext(value) {
53305
+ return value.length >= MIN_CIPHERTEXT_B64_LEN && CIPHERTEXT_CHARSET_RE.test(value);
53306
+ }
53307
+ async function decryptFieldsFailClosed(input2, paths, key) {
53308
+ return decryptFields(input2, paths, {
53309
+ decrypt: (ciphertext) => decryptField(ciphertext, key),
53310
+ isCiphertext: (value) => looksLikeCiphertext(value)
53311
+ });
53312
+ }
53313
+ var TXN_DECRYPT_PATHS = [
53314
+ ...ENCRYPTED_TXN_FIELDS,
53315
+ ...ENCRYPTED_MERCHANT_FIELDS.map((f) => `merchant.${f}`)
53316
+ ];
53228
53317
  async function decryptTransactionFields(items, key) {
53229
- for (const item of items) {
53230
- await decryptObjectFields(item, ENCRYPTED_TXN_FIELDS, key);
53231
- const merchant = item.merchant;
53232
- if (merchant && typeof merchant === "object") {
53233
- await decryptObjectFields(merchant, ENCRYPTED_MERCHANT_FIELDS, key);
53234
- }
53235
- }
53318
+ return decryptFieldsFailClosed(items, TXN_DECRYPT_PATHS, key);
53236
53319
  }
53237
53320
  async function decryptMerchantFields(items, key) {
53238
- for (const item of items) {
53239
- await decryptObjectFields(item, ENCRYPTED_MERCHANT_FIELDS, key);
53240
- }
53321
+ return decryptFieldsFailClosed(items, ENCRYPTED_MERCHANT_FIELDS, key);
53241
53322
  }
53242
53323
  async function encryptTransactionFields(items, key) {
53243
53324
  for (const item of items) {
53244
53325
  await encryptObjectFields(item, ENCRYPTED_TXN_FIELDS, key);
53245
53326
  }
53246
53327
  }
53328
+ async function decryptPersonCiphers(input2, key) {
53329
+ const { data, undecrypted } = await decryptFields(input2, ["cipher"], {
53330
+ decrypt: async (ciphertext) => {
53331
+ const json2 = await decryptField(ciphertext, key);
53332
+ JSON.parse(json2);
53333
+ return json2;
53334
+ }
53335
+ });
53336
+ const merge3 = (row) => {
53337
+ if (!row || typeof row !== "object")
53338
+ return;
53339
+ const r = row;
53340
+ if (typeof r.cipher !== "string")
53341
+ return;
53342
+ const fields = JSON.parse(r.cipher);
53343
+ Object.assign(r, fields, { cipher: null });
53344
+ };
53345
+ if (Array.isArray(data))
53346
+ data.forEach(merge3);
53347
+ else
53348
+ merge3(data);
53349
+ return { data, undecrypted };
53350
+ }
53247
53351
  var VERIFIER_PLAINTEXT = "paisa-verify-v1";
53248
53352
  async function checkVerifier(key, encryptedVerifier) {
53249
53353
  try {
@@ -53300,7 +53404,7 @@ async function initCrypto(client, pin, userId) {
53300
53404
  // ../../node_modules/.bun/@modelcontextprotocol+server@2.1.0/node_modules/@modelcontextprotocol/server/dist/index.mjs
53301
53405
  var DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024 * 1024;
53302
53406
 
53303
- // ../../node_modules/.bun/@nimit9+signet-server@0.2.2+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/http/status-codes.js
53407
+ // ../../node_modules/.bun/@nimit9+signet-server@0.2.3+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/http/status-codes.js
53304
53408
  var STATUS_CODES = {
53305
53409
  400: "BAD_REQUEST",
53306
53410
  401: "UNAUTHORIZED",
@@ -53314,7 +53418,7 @@ var STATUS_CODES = {
53314
53418
  429: "RATE_LIMITED"
53315
53419
  };
53316
53420
 
53317
- // ../../node_modules/.bun/@nimit9+signet-server@0.2.2+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/mcp/server.js
53421
+ // ../../node_modules/.bun/@nimit9+signet-server@0.2.3+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/mcp/server.js
53318
53422
  var RAW = Symbol.for("signet.mcp.rawResult");
53319
53423
  function toolResult(result) {
53320
53424
  return Object.defineProperty({ ...result }, RAW, { value: true, enumerable: false });
@@ -53398,10 +53502,17 @@ function toResult(value, structured) {
53398
53502
  return { content: [{ type: "text", text }] };
53399
53503
  }
53400
53504
  function createMcpServer(options) {
53505
+ const unlisted = new Set(options.tools.filter((t) => t.listed === false).map((t) => t.name));
53506
+ const toolsCapability = { tools: { listChanged: false } };
53401
53507
  const server = new McpServer({ name: options.name, version: options.version }, {
53402
- capabilities: { tools: { listChanged: false } },
53508
+ ...unlisted.size ? {} : { capabilities: toolsCapability },
53403
53509
  ...options.instructions ? { instructions: options.instructions } : {}
53404
53510
  });
53511
+ let restore;
53512
+ if (unlisted.size) {
53513
+ server.server.registerCapabilities(toolsCapability);
53514
+ restore = hideUnlisted(server, unlisted);
53515
+ }
53405
53516
  const register2 = server.registerTool.bind(server);
53406
53517
  const { c, token } = options.context ?? {};
53407
53518
  for (const tool of options.tools) {
@@ -53445,29 +53556,110 @@ function createMcpServer(options) {
53445
53556
  ...tool.input ? { inputSchema: tool.input } : {},
53446
53557
  ...tool.output ? { outputSchema: tool.output } : {}
53447
53558
  }, tool.input ? (input2, mcp) => run(input2, mcp) : (mcp) => run({}, mcp));
53559
+ restore?.();
53560
+ restore = undefined;
53448
53561
  }
53449
53562
  return server;
53450
53563
  }
53564
+ function hideUnlisted(server, unlisted) {
53565
+ const inner = server.server;
53566
+ const original = inner.setRequestHandler.bind(inner);
53567
+ let caught = false;
53568
+ inner.setRequestHandler = (method, handler) => {
53569
+ if (method !== "tools/list" || typeof handler !== "function")
53570
+ return original(method, handler);
53571
+ caught = true;
53572
+ original(method, async (request, ctx) => {
53573
+ const result = await handler(request, ctx);
53574
+ return { ...result, tools: result.tools.filter((t) => !unlisted.has(t.name)) };
53575
+ });
53576
+ };
53577
+ return () => {
53578
+ delete inner.setRequestHandler;
53579
+ if (!caught)
53580
+ throw new Error("signet mcp: could not hide unlisted tools; @modelcontextprotocol/server no longer installs tools/list through setRequestHandler");
53581
+ };
53582
+ }
53583
+
53584
+ // src/annotations.ts
53585
+ var RO = { readOnlyHint: true, openWorldHint: false };
53586
+ var WI = {
53587
+ readOnlyHint: false,
53588
+ destructiveHint: false,
53589
+ idempotentHint: true,
53590
+ openWorldHint: false
53591
+ };
53592
+ var W = {
53593
+ readOnlyHint: false,
53594
+ destructiveHint: false,
53595
+ idempotentHint: false,
53596
+ openWorldHint: false
53597
+ };
53598
+ var D = {
53599
+ readOnlyHint: false,
53600
+ destructiveHint: true,
53601
+ idempotentHint: false,
53602
+ openWorldHint: false
53603
+ };
53604
+ var WI_OPEN = {
53605
+ readOnlyHint: false,
53606
+ destructiveHint: false,
53607
+ idempotentHint: true,
53608
+ openWorldHint: true
53609
+ };
53610
+ var TOOL_META = {
53611
+ get_transactions: { title: "Search Transactions", annotations: RO },
53612
+ get_uncategorized_groups: { title: "Group Uncategorized Transactions", annotations: RO },
53613
+ categorize_groups: { title: "Categorize Transaction Groups", annotations: W },
53614
+ backfill_persons: { title: "Backfill Persons from Transactions", annotations: WI },
53615
+ update_transaction: { title: "Update Transaction", annotations: WI },
53616
+ bulk_link_entity: { title: "Bulk Link Transactions", annotations: WI },
53617
+ manage_duplicates: { title: "Manage Duplicate Transactions", annotations: W },
53618
+ get_reconciliation_context: { title: "Get Reconciliation Context", annotations: RO },
53619
+ bulk_categorize_transactions: { title: "Bulk Categorize Transactions", annotations: WI },
53620
+ delete_transactions: { title: "Delete Transactions", annotations: D },
53621
+ rescan_transactions: { title: "Rescan Transactions", annotations: WI },
53622
+ get_settlement_candidates: { title: "Find Settlement Candidates", annotations: RO },
53623
+ get_transaction: { title: "Get Transaction", annotations: RO },
53624
+ preview_import: { title: "Preview Statement Import", annotations: RO },
53625
+ import_statement: { title: "Import Statement", annotations: W },
53626
+ auto_categorize_jev: { title: "Auto-Categorize with Jev", annotations: W },
53627
+ list_entities: { title: "List Entities", annotations: RO },
53628
+ upsert_entity: { title: "Create or Update Entity", annotations: W },
53629
+ delete_entity: { title: "Delete Entity", annotations: D },
53630
+ merge_merchants: { title: "Merge Merchants", annotations: D },
53631
+ get_analytics: { title: "Get Analytics", annotations: RO },
53632
+ upsert_savings: { title: "Create or Update Savings Vehicle", annotations: W },
53633
+ contribute_to_goal: { title: "Contribute to Goal", annotations: W },
53634
+ set_budget: { title: "Set Budget", annotations: WI },
53635
+ get_dashboard: { title: "Get Dashboard", annotations: RO },
53636
+ sync_recurring: { title: "Sync Recurring Merchants", annotations: WI },
53637
+ check_alerts: { title: "Check Alerts", annotations: W },
53638
+ manage_alerts: { title: "Manage Alerts and Rules", annotations: WI },
53639
+ manage_debt: { title: "Add or Settle Debt", annotations: W },
53640
+ generate_monthly_report: { title: "Generate Monthly Report", annotations: WI },
53641
+ get_report: { title: "Get Report", annotations: RO },
53642
+ export_transactions: { title: "Export Transactions", annotations: RO },
53643
+ set_learning_rule: { title: "Set Learning Rule", annotations: WI },
53644
+ update_net_worth: { title: "Update Net Worth", annotations: WI },
53645
+ upsert_investment: { title: "Create or Update Investment", annotations: W },
53646
+ sync_zerodha: { title: "Sync Zerodha", annotations: WI_OPEN },
53647
+ get_settings: { title: "Get Settings", annotations: RO },
53648
+ update_settings: { title: "Update Settings", annotations: WI },
53649
+ set_encryption_mode: { title: "Set Encryption Mode", annotations: D }
53650
+ };
53651
+ function applyAnnotations(tools) {
53652
+ return tools.map((tool) => {
53653
+ const meta3 = TOOL_META[tool.name];
53654
+ if (!meta3)
53655
+ throw new Error(`No TOOL_META entry for tool "${tool.name}" — add one to annotations.ts`);
53656
+ return { ...tool, title: meta3.title, annotations: meta3.annotations };
53657
+ });
53658
+ }
53451
53659
  // src/lib/union-tool.ts
53452
53660
  function variant(input2, run) {
53453
53661
  return { input: input2, run };
53454
53662
  }
53455
- function isAlias(tool) {
53456
- return typeof tool.aliasFor === "string";
53457
- }
53458
- function aliasTool(name, input2, aliasFor, note, route) {
53459
- const tool = defineTool({
53460
- name,
53461
- description: `Deprecated alias of ${aliasFor}.`,
53462
- input: input2,
53463
- handler: async (args, ctx) => {
53464
- const [to, next] = route(args);
53465
- const parsed = to.input ? await to.input.parseAsync(next) : next;
53466
- return to.handler(parsed, ctx);
53467
- }
53468
- });
53469
- return { ...tool, aliasFor, aliasNote: note };
53470
- }
53471
53663
  function compactJsonSchema(schema) {
53472
53664
  const walk = (node2, root) => {
53473
53665
  if (Array.isArray(node2))
@@ -53564,91 +53756,7 @@ function unionTool(opts) {
53564
53756
  return v.run(rest, ctx);
53565
53757
  }
53566
53758
  });
53567
- return {
53568
- tool,
53569
- alias: (aliasName, value) => aliasTool(aliasName, variants[value].input, name, `${key}: "${value}"`, (a) => [
53570
- tool,
53571
- { ...a, [key]: value }
53572
- ])
53573
- };
53574
- }
53575
-
53576
- // src/annotations.ts
53577
- var RO = { readOnlyHint: true, openWorldHint: false };
53578
- var WI = {
53579
- readOnlyHint: false,
53580
- destructiveHint: false,
53581
- idempotentHint: true,
53582
- openWorldHint: false
53583
- };
53584
- var W = {
53585
- readOnlyHint: false,
53586
- destructiveHint: false,
53587
- idempotentHint: false,
53588
- openWorldHint: false
53589
- };
53590
- var D = {
53591
- readOnlyHint: false,
53592
- destructiveHint: true,
53593
- idempotentHint: false,
53594
- openWorldHint: false
53595
- };
53596
- var WI_OPEN = {
53597
- readOnlyHint: false,
53598
- destructiveHint: false,
53599
- idempotentHint: true,
53600
- openWorldHint: true
53601
- };
53602
- var TOOL_META = {
53603
- get_transactions: { title: "Search Transactions", annotations: RO },
53604
- get_uncategorized_groups: { title: "Group Uncategorized Transactions", annotations: RO },
53605
- categorize_groups: { title: "Categorize Transaction Groups", annotations: W },
53606
- backfill_persons: { title: "Backfill Persons from Transactions", annotations: WI },
53607
- update_transaction: { title: "Update Transaction", annotations: WI },
53608
- bulk_link_entity: { title: "Bulk Link Transactions", annotations: WI },
53609
- manage_duplicates: { title: "Manage Duplicate Transactions", annotations: W },
53610
- get_reconciliation_context: { title: "Get Reconciliation Context", annotations: RO },
53611
- bulk_categorize_transactions: { title: "Bulk Categorize Transactions", annotations: WI },
53612
- delete_transactions: { title: "Delete Transactions", annotations: D },
53613
- rescan_transactions: { title: "Rescan Transactions", annotations: WI },
53614
- get_settlement_candidates: { title: "Find Settlement Candidates", annotations: RO },
53615
- get_transaction: { title: "Get Transaction", annotations: RO },
53616
- preview_import: { title: "Preview Statement Import", annotations: RO },
53617
- import_statement: { title: "Import Statement", annotations: W },
53618
- auto_categorize_jev: { title: "Auto-Categorize with Jev", annotations: W },
53619
- list_entities: { title: "List Entities", annotations: RO },
53620
- upsert_entity: { title: "Create or Update Entity", annotations: W },
53621
- delete_entity: { title: "Delete Entity", annotations: D },
53622
- merge_merchants: { title: "Merge Merchants", annotations: D },
53623
- get_analytics: { title: "Get Analytics", annotations: RO },
53624
- upsert_savings: { title: "Create or Update Savings Vehicle", annotations: W },
53625
- contribute_to_goal: { title: "Contribute to Goal", annotations: W },
53626
- set_budget: { title: "Set Budget", annotations: WI },
53627
- get_dashboard: { title: "Get Dashboard", annotations: RO },
53628
- sync_recurring: { title: "Sync Recurring Merchants", annotations: WI },
53629
- check_alerts: { title: "Check Alerts", annotations: W },
53630
- manage_alerts: { title: "Manage Alerts and Rules", annotations: WI },
53631
- manage_debt: { title: "Add or Settle Debt", annotations: W },
53632
- generate_monthly_report: { title: "Generate Monthly Report", annotations: WI },
53633
- get_report: { title: "Get Report", annotations: RO },
53634
- export_transactions: { title: "Export Transactions", annotations: RO },
53635
- set_learning_rule: { title: "Set Learning Rule", annotations: WI },
53636
- update_net_worth: { title: "Update Net Worth", annotations: WI },
53637
- upsert_investment: { title: "Create or Update Investment", annotations: W },
53638
- sync_zerodha: { title: "Sync Zerodha", annotations: WI_OPEN },
53639
- get_settings: { title: "Get Settings", annotations: RO },
53640
- update_settings: { title: "Update Settings", annotations: WI },
53641
- set_encryption_mode: { title: "Set Encryption Mode", annotations: D }
53642
- };
53643
- function applyAnnotations(tools) {
53644
- return tools.map((tool) => {
53645
- if (isAlias(tool))
53646
- return tool;
53647
- const meta3 = TOOL_META[tool.name];
53648
- if (!meta3)
53649
- throw new Error(`No TOOL_META entry for tool "${tool.name}" — add one to annotations.ts`);
53650
- return { ...tool, title: meta3.title, annotations: meta3.annotations };
53651
- });
53759
+ return { tool };
53652
53760
  }
53653
53761
 
53654
53762
  // src/prompts.ts
@@ -53809,11 +53917,7 @@ function alertTools(client) {
53809
53917
  return { success: true, generated, unread };
53810
53918
  }
53811
53919
  }),
53812
- manage.tool,
53813
- manage.alias("dismiss_alert", "dismiss"),
53814
- manage.alias("mark_alert_read", "read"),
53815
- manage.alias("list_alert_rules", "list_rules"),
53816
- manage.alias("set_alert_rule", "set_rule")
53920
+ manage.tool
53817
53921
  ];
53818
53922
  }
53819
53923
 
@@ -53862,7 +53966,8 @@ var dashboardOutputSchema = exports_external.object({
53862
53966
  recentTransactions: exports_external.array(exports_external.record(exports_external.string(), exports_external.unknown())),
53863
53967
  netWorth: exports_external.record(exports_external.string(), exports_external.unknown()).nullable(),
53864
53968
  debtSummary: exports_external.object({ totalOutstanding: exports_external.number(), totalEmi: exports_external.number(), count: exports_external.number() }).nullable(),
53865
- budgetVsActual: exports_external.array(budgetVsActualEntrySchema)
53969
+ budgetVsActual: exports_external.array(budgetVsActualEntrySchema),
53970
+ undecrypted: exports_external.array(exports_external.string()).optional()
53866
53971
  });
53867
53972
  var spendingTrendsSchema = exports_external.object({
53868
53973
  success: exports_external.literal(true),
@@ -53897,7 +54002,8 @@ var spendingTrendsSchema = exports_external.object({
53897
54002
  total: exports_external.number(),
53898
54003
  count: exports_external.number()
53899
54004
  })),
53900
- budgetVsActual: exports_external.array(budgetVsActualEntrySchema)
54005
+ budgetVsActual: exports_external.array(budgetVsActualEntrySchema),
54006
+ undecrypted: exports_external.array(exports_external.string()).optional()
53901
54007
  });
53902
54008
  var categoryAnalyticsSchema = exports_external.object({
53903
54009
  success: exports_external.literal(true),
@@ -53912,7 +54018,7 @@ var categoryAnalyticsSchema = exports_external.object({
53912
54018
  recentTransactions: exports_external.array(exports_external.object({
53913
54019
  id: exports_external.string(),
53914
54020
  date: exports_external.string(),
53915
- description: exports_external.string(),
54021
+ description: exports_external.string().nullable(),
53916
54022
  amount: exports_external.number(),
53917
54023
  type: exports_external.string(),
53918
54024
  owner: exports_external.string(),
@@ -53926,7 +54032,8 @@ var categoryAnalyticsSchema = exports_external.object({
53926
54032
  total: exports_external.number(),
53927
54033
  count: exports_external.number()
53928
54034
  })),
53929
- stats: exports_external.object({ total: exports_external.number(), avgMonthly: exports_external.number(), count: exports_external.number() })
54035
+ stats: exports_external.object({ total: exports_external.number(), avgMonthly: exports_external.number(), count: exports_external.number() }),
54036
+ undecrypted: exports_external.array(exports_external.string()).optional()
53930
54037
  });
53931
54038
  var merchantAnalyticsSchema = exports_external.object({
53932
54039
  success: exports_external.literal(true),
@@ -53941,7 +54048,7 @@ var merchantAnalyticsSchema = exports_external.object({
53941
54048
  recentTransactions: exports_external.array(exports_external.object({
53942
54049
  id: exports_external.string(),
53943
54050
  date: exports_external.string(),
53944
- description: exports_external.string(),
54051
+ description: exports_external.string().nullable(),
53945
54052
  amount: exports_external.number(),
53946
54053
  type: exports_external.string(),
53947
54054
  owner: exports_external.string(),
@@ -53956,7 +54063,8 @@ var merchantAnalyticsSchema = exports_external.object({
53956
54063
  total: exports_external.number(),
53957
54064
  count: exports_external.number()
53958
54065
  })),
53959
- stats: exports_external.object({ total: exports_external.number(), avgMonthly: exports_external.number(), count: exports_external.number() })
54066
+ stats: exports_external.object({ total: exports_external.number(), avgMonthly: exports_external.number(), count: exports_external.number() }),
54067
+ undecrypted: exports_external.array(exports_external.string()).optional()
53960
54068
  });
53961
54069
  var analyticsOutputSchema = exports_external.union([
53962
54070
  spendingTrendsSchema,
@@ -53965,37 +54073,17 @@ var analyticsOutputSchema = exports_external.union([
53965
54073
  ]);
53966
54074
 
53967
54075
  // src/tools/analytics.ts
53968
- async function dec(value, key) {
53969
- if (typeof value !== "string" || !value)
53970
- return value;
53971
- try {
53972
- return await decryptField(value, key);
53973
- } catch {
53974
- return value;
53975
- }
53976
- }
53977
54076
  async function decryptAnalytics(data, key) {
53978
54077
  if (!data || typeof data !== "object")
53979
- return;
53980
- const d = data;
53981
- const topMerchants = d.topMerchants;
53982
- if (Array.isArray(topMerchants)) {
53983
- for (const m of topMerchants)
53984
- m.name = await dec(m.name, key);
53985
- }
53986
- const recent = d.recentTransactions;
53987
- if (Array.isArray(recent)) {
53988
- for (const t of recent) {
53989
- t.description = await dec(t.description, key);
53990
- if ("merchantName" in t)
53991
- t.merchantName = await dec(t.merchantName, key);
53992
- }
53993
- }
53994
- const merchant = d.merchant;
53995
- if (merchant && typeof merchant === "object") {
53996
- merchant.cleanName = await dec(merchant.cleanName, key);
53997
- merchant.rawId = await dec(merchant.rawId, key);
53998
- }
54078
+ return data;
54079
+ const { data: decrypted, undecrypted } = await decryptFieldsFailClosed(data, [
54080
+ "topMerchants.name",
54081
+ "recentTransactions.description",
54082
+ "recentTransactions.merchantName",
54083
+ "merchant.cleanName",
54084
+ "merchant.rawId"
54085
+ ], key);
54086
+ return undecrypted.length ? { ...decrypted, undecrypted } : decrypted;
53999
54087
  }
54000
54088
  function analyticsTools(client, crypto3) {
54001
54089
  return [
@@ -54023,7 +54111,7 @@ function analyticsTools(client, crypto3) {
54023
54111
  break;
54024
54112
  }
54025
54113
  if (crypto3)
54026
- await decryptAnalytics(data, crypto3.key);
54114
+ data = await decryptAnalytics(data, crypto3.key);
54027
54115
  return data;
54028
54116
  }
54029
54117
  })
@@ -54091,7 +54179,10 @@ function dashboardTools(client, crypto3) {
54091
54179
  handler: async (params) => {
54092
54180
  const data = await client.get("/api/dashboard", params.months ? { months: params.months } : undefined);
54093
54181
  if (crypto3 && Array.isArray(data?.recentTransactions)) {
54094
- await decryptTransactionFields(data.recentTransactions, crypto3.key);
54182
+ const { data: decrypted, undecrypted } = await decryptTransactionFields(data.recentTransactions, crypto3.key);
54183
+ data.recentTransactions = decrypted;
54184
+ if (undecrypted.length)
54185
+ return { ...data, undecrypted };
54095
54186
  }
54096
54187
  return data;
54097
54188
  }
@@ -54142,7 +54233,7 @@ function debtTools(client) {
54142
54233
  })
54143
54234
  }
54144
54235
  });
54145
- return [manage.tool, manage.alias("add_debt", "add"), manage.alias("settle_debt", "settle")];
54236
+ return [manage.tool];
54146
54237
  }
54147
54238
 
54148
54239
  // src/tools/accounts.ts
@@ -54643,13 +54734,17 @@ function merchantVariants(client, crypto3) {
54643
54734
  return {
54644
54735
  list: variant(exports_external.object({ rawId: exports_external.string().optional().describe("merchants: find one by UPI ID or name") }), async ({ rawId }) => {
54645
54736
  const data = await client.get("/api/merchants");
54646
- if (crypto3 && data.data)
54647
- await decryptMerchantFields(data.data, crypto3.key);
54737
+ let undecrypted = [];
54738
+ if (crypto3 && data.data) {
54739
+ const dec = await decryptMerchantFields(data.data, crypto3.key);
54740
+ data.data = dec.data;
54741
+ undecrypted = dec.undecrypted;
54742
+ }
54648
54743
  if (!rawId)
54649
- return data;
54744
+ return undecrypted.length ? { ...data, undecrypted } : data;
54650
54745
  const needle = rawId.toLowerCase();
54651
54746
  const merchants = (data.data ?? []).filter((m) => String(m.rawId ?? "").toLowerCase() === needle || String(m.cleanName ?? "").toLowerCase().includes(needle));
54652
- return { ...data, data: merchants };
54747
+ return { ...data, data: merchants, ...undecrypted.length ? { undecrypted } : {} };
54653
54748
  }),
54654
54749
  upsert: variant(exports_external.object({
54655
54750
  id: exports_external.string().optional().describe("Merchant UUID — pass to update, omit to create"),
@@ -54758,22 +54853,19 @@ function personVariants(client, crypto3) {
54758
54853
  if (id) {
54759
54854
  const data2 = await client.get(`/api/persons/${id}`);
54760
54855
  if (crypto3 && data2.data.cipher) {
54761
- const fields = await decryptPersonCipher(data2.data.cipher, crypto3.key);
54762
- Object.assign(data2.data, fields, { cipher: null });
54856
+ const { data: decrypted, undecrypted } = await decryptPersonCiphers(data2.data, crypto3.key);
54857
+ data2.data = decrypted;
54858
+ if (undecrypted.length)
54859
+ return { ...data2, undecrypted };
54763
54860
  }
54764
54861
  return data2;
54765
54862
  }
54766
54863
  const data = await client.get("/api/persons");
54767
54864
  if (crypto3 && data.data) {
54768
- for (const person of data.data) {
54769
- const cipher = person.cipher;
54770
- if (cipher) {
54771
- try {
54772
- const fields = await decryptPersonCipher(cipher, crypto3.key);
54773
- Object.assign(person, fields, { cipher: null });
54774
- } catch {}
54775
- }
54776
- }
54865
+ const { data: decrypted, undecrypted } = await decryptPersonCiphers(data.data, crypto3.key);
54866
+ data.data = decrypted;
54867
+ if (undecrypted.length)
54868
+ return { ...data, undecrypted };
54777
54869
  }
54778
54870
  return data;
54779
54871
  }),
@@ -54903,11 +54995,7 @@ function reportTools(client) {
54903
54995
  }),
54904
54996
  handler: (body) => client.post("/api/reports/generate", body)
54905
54997
  }),
54906
- report2.tool,
54907
- report2.alias("get_reports", "monthly"),
54908
- report2.alias("get_net_worth", "net_worth"),
54909
- report2.alias("get_upcoming", "upcoming"),
54910
- report2.alias("get_spending_heatmap", "heatmap")
54998
+ report2.tool
54911
54999
  ];
54912
55000
  }
54913
55001
 
@@ -54986,56 +55074,7 @@ function entityTools(client, crypto3) {
54986
55074
  fixed_deposit: investment.deleteFd
54987
55075
  }
54988
55076
  });
54989
- return [
54990
- list.tool,
54991
- upsert.tool,
54992
- del.tool,
54993
- list.alias("list_categories", "categories"),
54994
- list.alias("list_merchants", "merchants"),
54995
- list.alias("list_bank_accounts", "bank_accounts"),
54996
- list.alias("list_persons", "persons"),
54997
- list.alias("list_budgets", "budgets"),
54998
- list.alias("list_debts", "debts"),
54999
- list.alias("list_invites", "invites"),
55000
- list.alias("list_alerts", "alerts"),
55001
- list.alias("list_recurring", "recurring"),
55002
- list.alias("list_push_subscriptions", "push_subscriptions"),
55003
- list.alias("list_saved_reports", "saved_reports"),
55004
- aliasTool("list_learning", exports_external.object({ type: exports_external.enum(LEARNING_SLICES) }), "list_entities", 'entity: "learning", slice: <type>', ({ type }) => [list.tool, { entity: "learning", slice: type }]),
55005
- aliasTool("list_savings", exports_external.object({
55006
- type: exports_external.enum(["goals", "emis", "sinking_funds", "insurance", "contributions"]),
55007
- id: exports_external.string().optional()
55008
- }), "list_entities", "entity: <type> (contributions → goal_contributions)", ({ type, id }) => {
55009
- if (type !== "contributions")
55010
- return [list.tool, { entity: type }];
55011
- if (!id)
55012
- throw new Error("list_savings type='contributions' requires id (goal UUID)");
55013
- return [list.tool, { entity: "goal_contributions", id }];
55014
- }),
55015
- aliasTool("list_investments", exports_external.object({
55016
- type: exports_external.enum(["holdings", "sips", "fixed_deposits", "transactions"]),
55017
- id: exports_external.string().optional()
55018
- }), "list_entities", "entity: <type> (transactions → investment_transactions)", ({ type, id }) => {
55019
- if (type !== "transactions")
55020
- return [list.tool, { entity: type }];
55021
- if (!id)
55022
- throw new Error("list_investments type='transactions' requires id");
55023
- return [list.tool, { entity: "investment_transactions", id }];
55024
- }),
55025
- upsert.alias("upsert_merchant", "merchant"),
55026
- upsert.alias("upsert_person", "person"),
55027
- upsert.alias("upsert_category", "category"),
55028
- upsert.alias("upsert_bank_account", "bank_account"),
55029
- upsert.alias("upsert_insurance", "insurance"),
55030
- upsert.alias("create_invite", "invite"),
55031
- del.alias("delete_category", "category"),
55032
- del.alias("delete_merchant", "merchant"),
55033
- del.alias("delete_person", "person"),
55034
- del.alias("delete_debt", "debt"),
55035
- del.alias("revoke_invite", "invite"),
55036
- aliasTool("delete_savings", exports_external.object({ type: exports_external.enum(["goal", "emi", "insurance", "sinking_fund"]), id: exports_external.string() }), "delete_entity", "entity: <type>", ({ type, id }) => [del.tool, { entity: type, id }]),
55037
- aliasTool("delete_investment", exports_external.object({ type: exports_external.enum(["sip", "fixed_deposit"]), id: exports_external.string() }), "delete_entity", "entity: <type>", ({ type, id }) => [del.tool, { entity: type, id }])
55038
- ];
55077
+ return [list.tool, upsert.tool, del.tool];
55039
55078
  }
55040
55079
 
55041
55080
  // ../reconciliation/src/aliases.ts
@@ -58620,9 +58659,8 @@ async function fetchAllRowsPaged(client, crypto3, params = {}, maxPages = 100) {
58620
58659
  if (cursor)
58621
58660
  q2.cursor = cursor;
58622
58661
  const res = await client.get("/api/transactions", q2);
58623
- const batch = res.data ?? [];
58624
- if (crypto3)
58625
- await decryptTransactionFields(batch, crypto3.key);
58662
+ const batch0 = res.data ?? [];
58663
+ const batch = crypto3 ? (await decryptTransactionFields(batch0, crypto3.key)).data : batch0;
58626
58664
  out.push(...batch.map(toTxnRow));
58627
58665
  if (!res.hasMore || !res.nextCursor || batch.length === 0 || res.nextCursor === cursor)
58628
58666
  break;
@@ -59087,9 +59125,8 @@ async function runRescan(client, crypto3) {
59087
59125
  bag = (await loadBag(client)).bag;
59088
59126
  } catch {}
59089
59127
  const merchantsRes = await client.get("/api/merchants");
59090
- const allMerchants = merchantsRes.data ?? [];
59091
- if (crypto3)
59092
- await decryptMerchantFields(allMerchants, crypto3.key);
59128
+ const allMerchants0 = merchantsRes.data ?? [];
59129
+ const allMerchants = crypto3 ? (await decryptMerchantFields(allMerchants0, crypto3.key)).data : allMerchants0;
59093
59130
  const globalMerchants = allMerchants.filter((m2) => !m2.householdId && m2.cleanName).map((m2) => ({ id: m2.id, name: m2.cleanName }));
59094
59131
  const localMerchants = allMerchants.filter((m2) => m2.householdId && m2.cleanName).map((m2) => ({ id: m2.id, name: m2.cleanName }));
59095
59132
  const merchantCategoryById = new Map(allMerchants.filter((m2) => m2.categoryId).map((m2) => [m2.id, m2.categoryId]));
@@ -59186,9 +59223,13 @@ function transactionTools(client, crypto3) {
59186
59223
  if (!full)
59187
59224
  apiParams.view = "lean";
59188
59225
  const data = await client.get("/api/transactions", Object.fromEntries(Object.entries(apiParams).filter(([, v2]) => v2 !== undefined).map(([k2, v2]) => [k2, String(v2)])));
59189
- const rows = data.data ?? [];
59190
- if (crypto3)
59191
- await decryptTransactionFields(rows, crypto3.key);
59226
+ let rows = data.data ?? [];
59227
+ let undecrypted = [];
59228
+ if (crypto3) {
59229
+ const dec = await decryptTransactionFields(rows, crypto3.key);
59230
+ rows = dec.data;
59231
+ undecrypted = dec.undecrypted;
59232
+ }
59192
59233
  const personNames = new Map;
59193
59234
  if (rows.some((r2) => r2.personId)) {
59194
59235
  for (const p2 of await loadDecryptedPersons(client, crypto3))
@@ -59200,12 +59241,14 @@ function transactionTools(client, crypto3) {
59200
59241
  if (person && personNames.has(person.id))
59201
59242
  person.name = personNames.get(person.id) ?? "";
59202
59243
  }
59203
- return data;
59244
+ data.data = rows;
59245
+ return undecrypted.length ? { ...data, undecrypted } : data;
59204
59246
  }
59205
59247
  const out = {
59206
59248
  rows: rows.map((r2) => toLeanRow(r2, personNames)),
59207
59249
  ...data.hasMore ? { more: true, cursor: data.nextCursor } : {},
59208
- ...data.summary ? { spent: data.summary.spent, income: data.summary.income } : {}
59250
+ ...data.summary ? { spent: data.summary.spent, income: data.summary.income } : {},
59251
+ ...undecrypted.length ? { undecrypted } : {}
59209
59252
  };
59210
59253
  return out;
59211
59254
  }
@@ -59331,8 +59374,11 @@ function transactionTools(client, crypto3) {
59331
59374
  }
59332
59375
  if (action === "list") {
59333
59376
  const data2 = await client.get("/api/transactions/duplicates");
59334
- if (crypto3 && data2.data)
59335
- await decryptTransactionFields(data2.data, crypto3.key);
59377
+ if (crypto3 && data2.data) {
59378
+ const { data: decrypted, undecrypted } = await decryptTransactionFields(data2.data, crypto3.key);
59379
+ data2.data = decrypted;
59380
+ return withTruncationWarning(undecrypted.length ? { ...data2, undecrypted } : data2);
59381
+ }
59336
59382
  return withTruncationWarning(data2);
59337
59383
  }
59338
59384
  const data = await client.post("/api/transactions/duplicates/resolve", {
@@ -59352,8 +59398,11 @@ function transactionTools(client, crypto3) {
59352
59398
  }),
59353
59399
  handler: async ({ accountId, startDate, endDate }) => {
59354
59400
  const data = await client.get("/api/transactions/reconciliation-context", { accountId, startDate, endDate });
59355
- if (crypto3 && data.data)
59356
- await decryptTransactionFields(data.data, crypto3.key);
59401
+ if (crypto3 && data.data) {
59402
+ const { data: decrypted, undecrypted } = await decryptTransactionFields(data.data, crypto3.key);
59403
+ data.data = decrypted;
59404
+ return withTruncationWarning(undecrypted.length ? { ...data, undecrypted } : data);
59405
+ }
59357
59406
  return withTruncationWarning(data);
59358
59407
  }
59359
59408
  }),
@@ -59445,8 +59494,11 @@ function transactionTools(client, crypto3) {
59445
59494
  input: exports_external.object({ ccAccountId: exports_external.string().uuid() }),
59446
59495
  handler: async ({ ccAccountId }) => {
59447
59496
  const data = await client.get("/api/transactions/settlement-candidates", { ccAccountId });
59448
- if (crypto3 && data.data)
59449
- await decryptTransactionFields(data.data, crypto3.key);
59497
+ if (crypto3 && data.data) {
59498
+ const { data: decrypted, undecrypted } = await decryptTransactionFields(data.data, crypto3.key);
59499
+ data.data = decrypted;
59500
+ return withTruncationWarning(undecrypted.length ? { ...data, undecrypted } : data);
59501
+ }
59450
59502
  return withTruncationWarning(data);
59451
59503
  }
59452
59504
  }),
@@ -59456,11 +59508,15 @@ function transactionTools(client, crypto3) {
59456
59508
  input: exports_external.object({ id: exports_external.string().uuid() }),
59457
59509
  handler: async ({ id }) => {
59458
59510
  const res = await client.get(`/api/transactions/${id}`);
59459
- const row = res.data;
59511
+ let row = res.data;
59460
59512
  if (!row)
59461
59513
  return res;
59462
- if (crypto3)
59463
- await decryptTransactionFields([row], crypto3.key);
59514
+ let undecrypted = [];
59515
+ if (crypto3) {
59516
+ const dec = await decryptTransactionFields([row], crypto3.key);
59517
+ row = dec.data[0];
59518
+ undecrypted = dec.undecrypted;
59519
+ }
59464
59520
  const lean = Object.fromEntries([
59465
59521
  "id",
59466
59522
  "date",
@@ -59476,7 +59532,7 @@ function transactionTools(client, crypto3) {
59476
59532
  "isDuplicate",
59477
59533
  "source"
59478
59534
  ].filter((k2) => row[k2] !== undefined).map((k2) => [k2, row[k2]]));
59479
- return lean;
59535
+ return undecrypted.length ? { ...lean, undecrypted } : lean;
59480
59536
  }
59481
59537
  })
59482
59538
  ];
@@ -59501,7 +59557,9 @@ function exportTools(client, crypto3) {
59501
59557
  format: "json"
59502
59558
  });
59503
59559
  if (crypto3 && Array.isArray(data?.transactions)) {
59504
- await decryptTransactionFields(data.transactions, crypto3.key);
59560
+ const { data: decrypted, undecrypted } = await decryptTransactionFields(data.transactions, crypto3.key);
59561
+ data.transactions = decrypted;
59562
+ return withTruncationWarning(undecrypted.length ? { ...data, undecrypted } : data);
59505
59563
  }
59506
59564
  return withTruncationWarning(data);
59507
59565
  }
@@ -59509,603 +59567,231 @@ function exportTools(client, crypto3) {
59509
59567
  ];
59510
59568
  }
59511
59569
 
59512
- // ../../node_modules/.bun/@typesafe-ai+sdk@0.6.0/node_modules/@typesafe-ai/sdk/dist/index.mjs
59513
- var requestIdFrom = (headers) => headers.get("x-typesafe-request-id") ?? undefined;
59514
- var APIPromise = class APIPromise2 extends Promise {
59515
- #responsePromise;
59516
- #parseResponse;
59517
- #parsed;
59518
- constructor(responsePromise, parseResponse) {
59519
- super((resolve) => resolve(undefined));
59520
- this.#responsePromise = responsePromise;
59521
- this.#parseResponse = parseResponse;
59522
- }
59523
- asResponse() {
59524
- return this.#responsePromise;
59525
- }
59526
- async withResponse() {
59527
- const [data, response] = await Promise.all([this.#parse(), this.#responsePromise]);
59528
- return {
59529
- data,
59530
- response,
59531
- requestId: requestIdFrom(response.headers)
59532
- };
59533
- }
59534
- map(fn2) {
59535
- return new APIPromise2(this.#responsePromise, () => this.#parse().then(fn2));
59536
- }
59537
- #parse() {
59538
- this.#parsed ??= this.#responsePromise.then(this.#parseResponse);
59539
- return this.#parsed;
59540
- }
59541
- then(onfulfilled, onrejected) {
59542
- return this.#parse().then(onfulfilled, onrejected);
59543
- }
59544
- catch(onrejected) {
59545
- return this.#parse().catch(onrejected);
59546
- }
59547
- finally(onfinally) {
59548
- return this.#parse().finally(onfinally);
59549
- }
59550
- };
59551
- var ENV = {
59552
- apiKey: "TYPESAFE_API_KEY",
59553
- baseURL: "TYPESAFE_BASE_URL",
59554
- defaultModel: "TYPESAFE_DEFAULT_MODEL",
59555
- logLevel: "TYPESAFE_LOG_LEVEL"
59556
- };
59557
- var readEnv = (name) => {
59558
- if (typeof process === "undefined" || !process.env)
59559
- return;
59560
- return process.env[name]?.trim() || undefined;
59561
- };
59562
- var fromCodeOrEnv = (fromCode, envVar) => fromCode ?? readEnv(envVar);
59563
- var range = (from, to2) => Array.from({ length: to2 - from }, (_2, i2) => from + i2);
59564
- var DEFAULT_RETRY_POLICY = {
59565
- maxRetries: 2,
59566
- backoffInitialMs: 500,
59567
- backoffMaxMs: 5000,
59568
- backoffJitter: 0.25,
59569
- httpStatuses: /* @__PURE__ */ new Set([
59570
- 408,
59571
- 429,
59572
- ...range(500, 600)
59573
- ]),
59574
- respectRetryAfter: true,
59575
- maxRetryAfterMs: 60000,
59576
- apiConnectionError: true,
59577
- apiTimeoutError: true
59578
- };
59579
- DEFAULT_RETRY_POLICY.maxRetries;
59580
- var isRetryableStatus = (status, policy = DEFAULT_RETRY_POLICY) => policy.httpStatuses.has(status);
59581
- var parseRetryAfter = (headers, now = Date.now()) => {
59582
- const ms = Number(headers.get("retry-after-ms"));
59583
- if (headers.has("retry-after-ms") && Number.isFinite(ms) && ms >= 0)
59584
- return ms;
59585
- const raw = headers.get("retry-after");
59586
- if (raw === null)
59587
- return;
59588
- const seconds = Number(raw);
59589
- if (Number.isFinite(seconds))
59590
- return seconds >= 0 ? seconds * 1000 : undefined;
59591
- const date5 = Date.parse(raw);
59592
- if (!Number.isNaN(date5))
59593
- return Math.max(0, date5 - now);
59594
- };
59595
- var retryDelayMs = (attempt, headers, policy = DEFAULT_RETRY_POLICY, random = Math.random) => {
59596
- if (policy.respectRetryAfter && headers !== undefined) {
59597
- const retryAfter = parseRetryAfter(headers);
59598
- if (retryAfter !== undefined && retryAfter <= policy.maxRetryAfterMs)
59599
- return retryAfter;
59600
- }
59601
- const exponential = Math.min(policy.backoffInitialMs * 2 ** attempt, policy.backoffMaxMs);
59602
- return Math.round(exponential * (1 - random() * policy.backoffJitter));
59603
- };
59604
- var sleep3 = (ms, signal) => new Promise((resolve, reject) => {
59605
- if (signal?.aborted)
59606
- return reject(signal.reason);
59607
- const onAbort = () => {
59608
- clearTimeout(timer);
59609
- reject(signal?.reason);
59610
- };
59611
- const timer = setTimeout(() => {
59612
- signal?.removeEventListener("abort", onAbort);
59613
- resolve();
59614
- }, ms);
59615
- signal?.addEventListener("abort", onAbort, { once: true });
59616
- });
59617
- var TypeSafeError = class extends Error {
59618
- constructor(message, options) {
59619
- super(message, options);
59620
- this.name = new.target.name;
59621
- }
59622
- };
59623
- var isRecord = (value) => typeof value === "object" && value !== null;
59624
- var extractMessage = (body) => {
59625
- if (typeof body === "string")
59626
- return body || undefined;
59627
- if (!isRecord(body))
59628
- return;
59629
- const { error: error62, message, detail } = body;
59630
- if (typeof error62 === "string")
59631
- return error62;
59632
- if (isRecord(error62) && typeof error62.message === "string")
59633
- return error62.message;
59634
- if (typeof message === "string")
59635
- return message;
59636
- if (typeof detail === "string")
59637
- return detail;
59638
- if (isRecord(detail) && typeof detail.message === "string")
59639
- return detail.message;
59640
- if (Array.isArray(detail))
59641
- return describeValidationErrors(detail);
59642
- };
59643
- var describeValidationErrors = (errors3) => {
59644
- const parts = errors3.flatMap((e2) => {
59645
- if (!isRecord(e2) || typeof e2.msg !== "string")
59646
- return [];
59647
- const loc = Array.isArray(e2.loc) ? e2.loc.filter((x2) => x2 !== "body").join(".") : "";
59648
- return [loc ? `${loc}: ${e2.msg}` : e2.msg];
59649
- });
59650
- return parts.length > 0 ? parts.join("; ") : undefined;
59651
- };
59652
- var MAX_RAW_BODY_IN_MESSAGE = 200;
59653
- var APIError = class APIError2 extends TypeSafeError {
59654
- status;
59655
- headers;
59656
- body;
59657
- requestId;
59658
- constructor(status, body, headers, message) {
59659
- super(message ?? APIError2.describe(status, body));
59660
- this.status = status;
59661
- this.body = body;
59662
- this.headers = headers;
59663
- this.requestId = requestIdFrom(headers);
59664
- }
59665
- static describe(status, body) {
59666
- const detail = extractMessage(body);
59667
- if (detail)
59668
- return `${status} ${detail}`;
59669
- if (body === undefined)
59670
- return `${status} status code (no body)`;
59671
- const raw = typeof body === "string" ? body : JSON.stringify(body);
59672
- return `${status} ${raw.length > MAX_RAW_BODY_IN_MESSAGE ? `${raw.slice(0, MAX_RAW_BODY_IN_MESSAGE)}…` : raw}`;
59673
- }
59674
- static fromResponse(status, body, headers) {
59675
- if (status === 400)
59676
- return new BadRequestError(status, body, headers);
59677
- if (status === 401)
59678
- return new AuthenticationError(status, body, headers);
59679
- if (status === 403)
59680
- return new PermissionDeniedError(status, body, headers);
59681
- if (status === 404)
59682
- return new NotFoundError(status, body, headers);
59683
- if (status === 422)
59684
- return new UnprocessableEntityError(status, body, headers);
59685
- if (status === 429)
59686
- return new RateLimitError(status, body, headers);
59687
- if (status >= 500)
59688
- return new InternalServerError(status, body, headers);
59689
- return new APIError2(status, body, headers);
59690
- }
59691
- };
59692
- var BadRequestError = class extends APIError {
59693
- };
59694
- var AuthenticationError = class extends APIError {
59695
- };
59696
- var PermissionDeniedError = class extends APIError {
59697
- };
59698
- var NotFoundError = class extends APIError {
59699
- };
59700
- var UnprocessableEntityError = class extends APIError {
59701
- };
59702
- var RateLimitError = class extends APIError {
59703
- retryAfterMs = parseRetryAfter(this.headers);
59704
- };
59705
- var InternalServerError = class extends APIError {
59706
- };
59707
- var APIConnectionError = class extends TypeSafeError {
59708
- constructor(message = "Connection error.", options) {
59709
- super(message, options);
59710
- }
59711
- };
59712
- var APITimeoutError = class extends APIConnectionError {
59713
- timeoutMs;
59714
- constructor(timeoutMs, options) {
59715
- super(`Request timed out after ${timeoutMs}ms.`, options);
59716
- this.timeoutMs = timeoutMs;
59717
- }
59718
- };
59719
- var APIUserAbortError = class extends TypeSafeError {
59720
- constructor(message = "Request was aborted.", options) {
59721
- super(message, options);
59722
- }
59723
- };
59724
- var LOG_LEVELS = [
59725
- "debug",
59726
- "info",
59727
- "warn",
59728
- "error",
59729
- "off"
59730
- ];
59731
- var DEFAULT_LOG_LEVEL = "warn";
59732
- var isLogLevel = (value) => LOG_LEVELS.includes(value);
59733
- var parseLogLevel = (value, source) => {
59734
- if (isLogLevel(value))
59735
- return value;
59736
- throw new TypeSafeError(`Invalid log level "${value}" from ${source}. Expected one of: ${LOG_LEVELS.join(", ")}.`);
59737
- };
59738
- var PREFIX = "[typesafe-sdk]";
59739
- var consoleLogger = {
59740
- debug: (message, ...args) => console.debug(`${PREFIX} ${message}`, ...args),
59741
- info: (message, ...args) => console.info(`${PREFIX} ${message}`, ...args),
59742
- warn: (message, ...args) => console.warn(`${PREFIX} ${message}`, ...args),
59743
- error: (message, ...args) => console.error(`${PREFIX} ${message}`, ...args)
59744
- };
59745
- var RANK = {
59746
- debug: 0,
59747
- info: 1,
59748
- warn: 2,
59749
- error: 3,
59750
- off: 4
59751
- };
59752
- var drop = () => {};
59753
- var withLevel = (sink, level) => {
59754
- const enabled = (at2) => RANK[at2] >= RANK[level];
59755
- return {
59756
- debug: enabled("debug") ? (message, ...args) => sink.debug(message, ...args) : drop,
59757
- info: enabled("info") ? (message, ...args) => sink.info(message, ...args) : drop,
59758
- warn: enabled("warn") ? (message, ...args) => sink.warn(message, ...args) : drop,
59759
- error: enabled("error") ? (message, ...args) => sink.error(message, ...args) : drop
59760
- };
59761
- };
59762
- var KEY_HEADERS = /* @__PURE__ */ new Set([
59763
- "authorization",
59764
- "proxy-authorization",
59765
- "x-api-key"
59766
- ]);
59767
- var OPAQUE_HEADERS = /* @__PURE__ */ new Set(["cookie", "set-cookie"]);
59768
- var redactKey = (value) => {
59769
- const [scheme, secret] = value.includes(" ") ? value.split(/\s+/, 2) : [undefined, value];
59770
- const tail = secret && secret.length > 8 ? secret.slice(-4) : "";
59771
- return `${scheme ? `${scheme} ` : ""}***${tail}`;
59772
- };
59773
- var redact = (name, value) => {
59774
- const lower = name.toLowerCase();
59775
- if (KEY_HEADERS.has(lower))
59776
- return redactKey(value);
59777
- if (OPAQUE_HEADERS.has(lower))
59778
- return "***";
59779
- return value;
59780
- };
59781
- var redactHeaders = (headers) => Object.fromEntries(Object.entries(headers).map(([name, value]) => [name, redact(name, value)]));
59782
- var choice = (instructions, criteria) => {
59783
- if (Array.isArray(criteria))
59784
- throw new TypeSafeError("Choice criteria must be a map of labels to descriptions, not a list.");
59785
- return {
59786
- type: "choice",
59787
- instructions,
59788
- criteria
59789
- };
59790
- };
59791
- var validateQuestions = (questions) => {
59792
- if (Object.keys(questions).length === 0)
59793
- throw new TypeSafeError("At least one question is required.");
59794
- for (const [name, question] of Object.entries(questions)) {
59795
- if (question.type !== "score")
59796
- continue;
59797
- if (!Array.isArray(question.criteria))
59798
- throw new TypeSafeError(`Score question "${name}" has criteria that are not a list; score criteria must be a list of descriptions indexed by score from zero.`);
59799
- if (question.criteria.length < 2)
59800
- throw new TypeSafeError(`Score question "${name}" has ${question.criteria.length} criteria; at least two scores are required.`);
59801
- }
59802
- };
59803
- var Models = class {
59804
- #transport;
59805
- constructor(transport) {
59806
- this.#transport = transport;
59807
- }
59808
- list(options = {}) {
59809
- return this.#transport.request("GET", "/v1/models", options).map(unwrapModels);
59810
- }
59811
- };
59812
- var unwrapModels = (wire) => {
59813
- if (Array.isArray(wire?.models))
59814
- return wire.models;
59815
- throw new TypeSafeError("Unexpected response shape from GET /v1/models; expected { models: [...] }.");
59816
- };
59817
- var g2 = globalThis;
59818
- var isBrowser = () => typeof g2.window !== "undefined" && typeof g2.window.document !== "undefined" && typeof g2.navigator !== "undefined";
59819
- var describeRuntime = () => {
59820
- const platform = g2.process?.platform && g2.process?.arch ? ` (${g2.process.platform}; ${g2.process.arch})` : "";
59821
- if (g2.Bun?.version)
59822
- return `bun/${g2.Bun.version}${platform}`;
59823
- if (g2.Deno?.version?.deno)
59824
- return `deno/${g2.Deno.version.deno}${platform}`;
59825
- if (g2.EdgeRuntime !== undefined)
59826
- return "vercel-edge";
59827
- if (g2.navigator?.userAgent === "Cloudflare-Workers")
59828
- return "cloudflare-workers";
59829
- if (g2.process?.versions?.node)
59830
- return `node/${g2.process.versions.node}${platform}`;
59831
- if (isBrowser())
59832
- return "browser";
59833
- return "unknown";
59834
- };
59835
- var VERSION2 = "0.6.0";
59836
- var missingApiKey = () => {
59837
- throw new TypeSafeError(`No API key was provided. Pass \`apiKey\` to the TypeSafeClient constructor or set the ${ENV.apiKey} environment variable.`);
59838
- };
59839
- var missingFetch = () => {
59840
- throw new TypeSafeError("No global `fetch` is available in this runtime. Pass a `fetch` implementation to the TypeSafeClient constructor.");
59841
- };
59842
- var refuseBrowser = () => {
59843
- throw new TypeSafeError("TypeSafeClient is running in a browser, which would expose your API key to anyone using the page. Call the API from a server instead, or pass `dangerouslyAllowBrowser: true` if you understand the risk.");
59844
- };
59845
- var defaultFetch = (input2, init) => globalThis.fetch(input2, init);
59846
- var assertNonNegativeInteger = (name, value) => {
59847
- if (!Number.isInteger(value) || value < 0)
59848
- throw new TypeSafeError(`\`${name}\` must be a non-negative integer, got ${String(value)}.`);
59849
- return value;
59850
- };
59851
- var assertPositiveMs = (name, value) => {
59852
- if (!Number.isFinite(value) || value <= 0)
59853
- throw new TypeSafeError(`\`${name}\` must be a positive number of milliseconds, got ${String(value)}.`);
59854
- return value;
59855
- };
59856
- var assertNonNegativeMs = (name, value) => {
59857
- if (!Number.isFinite(value) || value < 0)
59858
- throw new TypeSafeError(`\`${name}\` must be a non-negative number of milliseconds, got ${String(value)}.`);
59859
- return value;
59860
- };
59861
- var assertFraction = (name, value) => {
59862
- if (!Number.isFinite(value) || value < 0 || value > 1)
59863
- throw new TypeSafeError(`\`${name}\` must be between 0 and 1, got ${String(value)}.`);
59864
- return value;
59865
- };
59866
- var assertStatusSet = (name, statuses) => {
59867
- for (const status of statuses)
59868
- if (!Number.isInteger(status) || status < 100 || status > 999)
59869
- throw new TypeSafeError(`\`${name}\` must contain HTTP status codes, got ${String(status)}.`);
59870
- return statuses;
59871
- };
59872
- var resolveRetryPolicy = (base, overrides) => {
59873
- const o2 = overrides ?? {};
59874
- return {
59875
- maxRetries: o2.maxRetries === undefined ? base.maxRetries : assertNonNegativeInteger("retry.maxRetries", o2.maxRetries),
59876
- backoffInitialMs: o2.backoffInitialMs === undefined ? base.backoffInitialMs : assertNonNegativeMs("retry.backoffInitialMs", o2.backoffInitialMs),
59877
- backoffMaxMs: o2.backoffMaxMs === undefined ? base.backoffMaxMs : assertNonNegativeMs("retry.backoffMaxMs", o2.backoffMaxMs),
59878
- backoffJitter: o2.backoffJitter === undefined ? base.backoffJitter : assertFraction("retry.backoffJitter", o2.backoffJitter),
59879
- httpStatuses: new Set(o2.httpStatuses === undefined ? base.httpStatuses : assertStatusSet("retry.httpStatuses", o2.httpStatuses)),
59880
- respectRetryAfter: o2.respectRetryAfter ?? base.respectRetryAfter,
59881
- maxRetryAfterMs: o2.maxRetryAfterMs === undefined ? base.maxRetryAfterMs : assertNonNegativeMs("retry.maxRetryAfterMs", o2.maxRetryAfterMs),
59882
- apiConnectionError: o2.apiConnectionError ?? base.apiConnectionError,
59883
- apiTimeoutError: o2.apiTimeoutError ?? base.apiTimeoutError
59884
- };
59885
- };
59886
- var isRetryableError = (err, policy) => {
59887
- if (err instanceof APITimeoutError)
59888
- return policy.apiTimeoutError;
59889
- if (err instanceof APIConnectionError)
59890
- return policy.apiConnectionError;
59891
- return false;
59892
- };
59893
- var resolveLogLevel = (fromCode) => {
59894
- if (fromCode !== undefined)
59895
- return parseLogLevel(fromCode, "the `logLevel` option");
59896
- const fromEnv = readEnv(ENV.logLevel);
59897
- if (fromEnv !== undefined)
59898
- return parseLogLevel(fromEnv, ENV.logLevel);
59899
- return DEFAULT_LOG_LEVEL;
59900
- };
59901
- var stripTrailingSlashes = (url3) => url3.replace(/\/+$/, "");
59902
- var mergeHeaders = (...sources) => {
59903
- const entries = /* @__PURE__ */ new Map;
59904
- for (const source of sources)
59905
- for (const [name, value] of Object.entries(source))
59906
- if (value === undefined)
59907
- entries.delete(name.toLowerCase());
59908
- else
59909
- entries.set(name.toLowerCase(), [name, value]);
59910
- return Object.fromEntries(entries.values());
59911
- };
59912
- var bufferResponse = async (response, signal) => {
59913
- const reader = response.clone().body?.getReader();
59914
- if (!reader)
59915
- return;
59916
- const cancel = () => {
59917
- reader.cancel(signal.reason).catch(() => {});
59918
- response.body?.cancel(signal.reason).catch(() => {});
59919
- };
59920
- signal.addEventListener("abort", cancel, { once: true });
59921
- try {
59922
- if (signal.aborted)
59923
- cancel();
59924
- signal.throwIfAborted();
59925
- while (!(await reader.read()).done)
59926
- signal.throwIfAborted();
59927
- signal.throwIfAborted();
59928
- } finally {
59929
- signal.removeEventListener("abort", cancel);
59930
- reader.releaseLock();
59931
- }
59932
- };
59933
- var RUNTIME = describeRuntime();
59934
- var TypeSafeClient = class {
59935
- #apiKey;
59936
- baseURL;
59937
- defaultModel;
59938
- logLevel;
59939
- logger;
59940
- retry;
59941
- timeout;
59942
- defaultHeaders;
59943
- fetch;
59944
- models;
59945
- #requestCount = 0;
59946
- constructor(config2 = {}) {
59947
- if (isBrowser() && !config2.dangerouslyAllowBrowser)
59948
- refuseBrowser();
59949
- this.#apiKey = fromCodeOrEnv(config2.apiKey, ENV.apiKey) ?? missingApiKey();
59950
- this.baseURL = stripTrailingSlashes(fromCodeOrEnv(config2.baseURL, ENV.baseURL) ?? "https://api.typesafe.ai");
59951
- this.defaultModel = fromCodeOrEnv(config2.defaultModel, ENV.defaultModel) ?? "jev-latest";
59952
- this.logLevel = resolveLogLevel(config2.logLevel);
59953
- this.logger = withLevel(config2.logger ?? consoleLogger, this.logLevel);
59954
- this.retry = resolveRetryPolicy(DEFAULT_RETRY_POLICY, config2.retry);
59955
- this.timeout = assertPositiveMs("timeout", config2.timeout ?? 1e4);
59956
- this.defaultHeaders = { ...config2.defaultHeaders };
59957
- if (config2.fetch === undefined && typeof globalThis.fetch !== "function")
59958
- missingFetch();
59959
- this.fetch = config2.fetch ?? defaultFetch;
59960
- const transport = {
59961
- request: (method, path, options) => this.#request(method, path, options),
59962
- defaultModel: this.defaultModel
59963
- };
59964
- this.models = new Models(transport);
59965
- }
59966
- systemOne(request, options = {}) {
59967
- validateQuestions(request.questions);
59968
- const body = {
59969
- ...request,
59970
- model: request.model ?? this.defaultModel
59971
- };
59972
- return this.#request("POST", "/v1/systemone", {
59973
- ...options,
59974
- body
59975
- });
59570
+ // ../../node_modules/.bun/@nimit9+signet-ai@0.1.11+53d0ad1559459462/node_modules/@nimit9/signet-ai/dist/jev.js
59571
+ var JEV_DEFAULT_MODEL = "jev-1.13.0";
59572
+ var JEV_DEFAULT_MIN_CONFIDENCE = 0.95;
59573
+ var JEV_DEFAULT_TIMEOUT_MS = 8000;
59574
+ var JEV_DEFAULT_BASE_URL = "https://api.typesafe.ai";
59575
+ function fallbackOf(q2, reason, extra = {}) {
59576
+ const d2 = { value: q2.fallback, confident: false, source: "fallback", reason };
59577
+ if (extra.confidence !== undefined)
59578
+ d2.confidence = extra.confidence;
59579
+ if (extra.answer !== undefined)
59580
+ d2.answer = extra.answer;
59581
+ if (extra.probability !== undefined)
59582
+ d2.probability = extra.probability;
59583
+ return d2;
59584
+ }
59585
+ function labelsOf(options) {
59586
+ return Array.isArray(options) ? [...options] : Object.keys(options);
59587
+ }
59588
+ function isUnit(n2) {
59589
+ return typeof n2 === "number" && Number.isFinite(n2) && n2 >= 0 && n2 <= 1;
59590
+ }
59591
+ function validQuestion(q2) {
59592
+ if (!q2 || typeof q2.question !== "string")
59593
+ return false;
59594
+ if (q2.type === "choice") {
59595
+ const labels = labelsOf(q2.options);
59596
+ return labels.length >= 1 && labels.length <= 255 && labels.includes(q2.fallback);
59597
+ }
59598
+ if (q2.type === "score") {
59599
+ return Array.isArray(q2.levels) && q2.levels.length >= 2 && q2.levels.length <= 10;
59600
+ }
59601
+ return q2.type === "noul";
59602
+ }
59603
+ function toWire(q2) {
59604
+ if (q2.type === "choice") {
59605
+ const criteria = Array.isArray(q2.options) ? Object.fromEntries(q2.options.map((l2) => [l2, null])) : q2.options;
59606
+ return { type: "choice", instructions: q2.question, criteria };
59607
+ }
59608
+ if (q2.type === "score")
59609
+ return { type: "score", instructions: q2.question, criteria: q2.levels };
59610
+ return { type: "noul", instructions: q2.question };
59611
+ }
59612
+ function judge(q2, raw, min) {
59613
+ const a2 = raw;
59614
+ if (!a2 || typeof a2 !== "object")
59615
+ return fallbackOf(q2, "invalid_response");
59616
+ if (q2.type === "noul") {
59617
+ const p2 = a2.noul;
59618
+ if (!isUnit(p2))
59619
+ return fallbackOf(q2, "invalid_response");
59620
+ const confidence2 = Math.max(p2, 1 - p2);
59621
+ const answer2 = p2 >= 0.5;
59622
+ if (confidence2 < min) {
59623
+ return fallbackOf(q2, "low_confidence", { confidence: confidence2, answer: answer2, probability: p2 });
59624
+ }
59625
+ return { value: answer2, confident: true, source: "jev", confidence: confidence2, probability: p2 };
59626
+ }
59627
+ const confidence = a2.confidence;
59628
+ if (!isUnit(confidence))
59629
+ return fallbackOf(q2, "invalid_response");
59630
+ let answer;
59631
+ if (q2.type === "choice") {
59632
+ if (typeof a2.choice !== "string" || !labelsOf(q2.options).includes(a2.choice)) {
59633
+ return fallbackOf(q2, "invalid_response", { confidence });
59634
+ }
59635
+ answer = a2.choice;
59636
+ } else {
59637
+ const s2 = a2.score;
59638
+ if (typeof s2 !== "number" || !Number.isFinite(s2) || s2 < 0 || s2 > q2.levels.length - 1) {
59639
+ return fallbackOf(q2, "invalid_response", { confidence });
59640
+ }
59641
+ answer = s2;
59976
59642
  }
59977
- #request(method, path, options = {}) {
59978
- const resolved = {
59979
- method,
59980
- path,
59981
- body: options.body,
59982
- headers: mergeHeaders(this.defaultHeaders, options.headers ?? {}),
59983
- signal: options.signal,
59984
- timeout: options.timeout === undefined ? this.timeout : assertPositiveMs("timeout", options.timeout),
59985
- retry: resolveRetryPolicy(this.retry, options.retry)
59986
- };
59987
- const tag = `#${++this.#requestCount} ${method} ${path}`;
59988
- return new APIPromise(this.fetchWithRetries(tag, resolved), async (res) => {
59989
- const parsed = await parseBody(res);
59990
- this.logger.debug(`${tag} <- body`, parsed);
59991
- return parsed;
59992
- });
59643
+ if (confidence < min)
59644
+ return fallbackOf(q2, "low_confidence", { confidence, answer });
59645
+ return { value: answer, confident: true, source: "jev", confidence };
59646
+ }
59647
+
59648
+ class Timeout {
59649
+ }
59650
+ function createJev(config2) {
59651
+ const apiKey = typeof config2.apiKey === "string" ? config2.apiKey.trim() : "";
59652
+ const model = config2.model ?? JEV_DEFAULT_MODEL;
59653
+ const floor = config2.minConfidence ?? JEV_DEFAULT_MIN_CONFIDENCE;
59654
+ const timeoutMs = config2.timeoutMs ?? JEV_DEFAULT_TIMEOUT_MS;
59655
+ const allowSensitive = config2.allowSensitive === true;
59656
+ const url3 = `${(config2.baseUrl ?? JEV_DEFAULT_BASE_URL).replace(/\/+$/, "")}/v1/systemone`;
59657
+ function emit(event) {
59658
+ if (!config2.onEvent)
59659
+ return;
59660
+ try {
59661
+ config2.onEvent(event);
59662
+ } catch {}
59993
59663
  }
59994
- async fetchWithRetries(tag, req) {
59995
- const url3 = `${this.baseURL}${req.path}`;
59996
- const headers = mergeHeaders(req.headers, {
59997
- Authorization: `Bearer ${this.#apiKey}`,
59998
- Accept: "application/json",
59999
- "User-Agent": `typesafe-sdk/${VERSION2}`,
60000
- "X-TypeSafe-SDK": `typesafe-sdk/${VERSION2}`,
60001
- "X-TypeSafe-Runtime": RUNTIME,
60002
- "Content-Type": req.body === undefined ? undefined : "application/json",
60003
- "X-TypeSafe-Retry-Count": undefined
60004
- });
60005
- const body = req.body === undefined ? undefined : JSON.stringify(req.body);
60006
- for (let attempt = 0;; attempt++) {
60007
- const retriesLeft = req.retry.maxRetries - attempt;
60008
- const attemptHeaders = attempt === 0 ? headers : {
60009
- ...headers,
60010
- "X-TypeSafe-Retry-Count": String(attempt)
59664
+ function finish(questions, decisions, meta3) {
59665
+ const durationMs = Date.now() - meta3.started;
59666
+ for (const [name, d2] of Object.entries(decisions)) {
59667
+ const event = {
59668
+ kind: questions[name].type,
59669
+ name,
59670
+ source: d2.source,
59671
+ sensitive: meta3.sensitive,
59672
+ model,
59673
+ durationMs
60011
59674
  };
60012
- this.logger.debug(`${tag} -> ${url3}`, {
60013
- headers: redactHeaders(attemptHeaders),
60014
- body: req.body
60015
- });
60016
- const started = Date.now();
60017
- let res;
60018
- try {
60019
- res = await this.attempt(tag, url3, {
60020
- method: req.method,
60021
- headers: attemptHeaders,
60022
- body
60023
- }, req);
60024
- } catch (err) {
60025
- if (err instanceof APIUserAbortError || retriesLeft <= 0)
60026
- throw err;
60027
- if (!isRetryableError(err, req.retry))
60028
- throw err;
60029
- await this.backOff(tag, attempt, retriesLeft, err.message, undefined, req);
60030
- continue;
60031
- }
60032
- const requestId = requestIdFrom(res.headers);
60033
- this.logger.info(`${tag} <- ${res.status} in ${Date.now() - started}ms${requestId ? ` (request ${requestId})` : ""}`);
60034
- if (res.ok)
60035
- return res;
60036
- const errorBody = await parseBody(res);
60037
- this.logger.debug(`${tag} <- error body`, errorBody);
60038
- const error62 = APIError.fromResponse(res.status, errorBody, res.headers);
60039
- if (retriesLeft <= 0 || !isRetryableStatus(res.status, req.retry))
60040
- throw error62;
60041
- await this.backOff(tag, attempt, retriesLeft, `${res.status}`, res.headers, req);
59675
+ if (d2.reason)
59676
+ event.reason = d2.reason;
59677
+ if (d2.confidence !== undefined)
59678
+ event.confidence = d2.confidence;
59679
+ if (meta3.status !== undefined && d2.reason === "http_error")
59680
+ event.status = meta3.status;
59681
+ emit(event);
60042
59682
  }
59683
+ return decisions;
60043
59684
  }
60044
- async attempt(tag, url3, init, { signal, timeout }) {
60045
- const controller = new AbortController;
60046
- const abortFromCaller = () => controller.abort(signal?.reason);
60047
- if (signal?.aborted)
60048
- abortFromCaller();
60049
- signal?.addEventListener("abort", abortFromCaller, { once: true });
60050
- let timedOut = false;
60051
- const timer = setTimeout(() => {
60052
- timedOut = true;
60053
- controller.abort();
60054
- }, timeout);
59685
+ async function run(questions, base) {
60055
59686
  const started = Date.now();
60056
- const elapsed = () => `${Date.now() - started}ms`;
59687
+ const sensitive = base.sensitive === true;
59688
+ const all2 = (reason, status2) => finish(questions, Object.fromEntries(Object.entries(questions).map(([n2, q2]) => [n2, fallbackOf(q2, reason)])), { sensitive, started, status: status2 });
59689
+ if (base.sensitive !== false && !allowSensitive)
59690
+ return all2("sensitive_not_allowed");
59691
+ if (!apiKey)
59692
+ return all2("no_key");
59693
+ const min = base.minConfidence ?? floor;
59694
+ const names = Object.keys(questions);
59695
+ if (!isUnit(min) || names.length === 0 || !names.every((n2) => validQuestion(questions[n2]))) {
59696
+ return all2("invalid_request");
59697
+ }
59698
+ if (base.signal?.aborted)
59699
+ return all2("aborted");
59700
+ const controller = new AbortController;
59701
+ const onAbort = () => controller.abort();
59702
+ base.signal?.addEventListener("abort", onAbort, { once: true });
59703
+ let timer;
59704
+ const timeout = new Promise((resolve) => {
59705
+ timer = setTimeout(() => {
59706
+ controller.abort();
59707
+ resolve(new Timeout);
59708
+ }, timeoutMs);
59709
+ });
59710
+ const doFetch = config2.fetch ?? ((u2, i2) => globalThis.fetch(u2, i2));
59711
+ let status;
60057
59712
  try {
60058
- const response = await this.fetch(url3, {
60059
- ...init,
60060
- signal: controller.signal
60061
- });
60062
- await bufferResponse(response, controller.signal);
60063
- return response;
59713
+ const request = (async () => {
59714
+ const res = await doFetch(url3, {
59715
+ method: "POST",
59716
+ headers: {
59717
+ Authorization: `Bearer ${apiKey}`,
59718
+ Accept: "application/json",
59719
+ "Content-Type": "application/json"
59720
+ },
59721
+ body: JSON.stringify({
59722
+ model,
59723
+ state: base.state,
59724
+ questions: Object.fromEntries(names.map((n2) => [n2, toWire(questions[n2])]))
59725
+ }),
59726
+ signal: controller.signal
59727
+ });
59728
+ status = res.status;
59729
+ if (!res.ok)
59730
+ return { ok: false };
59731
+ return { ok: true, body: await res.json() };
59732
+ })();
59733
+ const out = await Promise.race([request, timeout]);
59734
+ if (out instanceof Timeout) {
59735
+ request.catch(() => {});
59736
+ return all2("timeout");
59737
+ }
59738
+ if (!out.ok)
59739
+ return all2("http_error", status);
59740
+ const answers = out.body?.answers;
59741
+ if (!answers || typeof answers !== "object")
59742
+ return all2("invalid_response");
59743
+ return finish(questions, Object.fromEntries(names.map((n2) => [n2, judge(questions[n2], answers[n2], min)])), { sensitive, started });
60064
59744
  } catch (err) {
60065
- if (signal?.aborted) {
60066
- this.logger.info(`${tag} aborted by caller after ${elapsed()}`);
60067
- throw new APIUserAbortError(undefined, { cause: err });
60068
- }
60069
- if (timedOut) {
60070
- this.logger.info(`${tag} timed out after ${elapsed()}`);
60071
- throw new APITimeoutError(timeout, { cause: err });
60072
- }
60073
- this.logger.info(`${tag} connection error after ${elapsed()}`, err);
60074
- throw new APIConnectionError(err instanceof Error ? `Connection error: ${err.message}` : undefined, { cause: err });
59745
+ if (base.signal?.aborted)
59746
+ return all2("aborted");
59747
+ if (status !== undefined)
59748
+ return all2("invalid_response");
59749
+ return all2(err?.name === "TimeoutError" ? "timeout" : "network_error");
60075
59750
  } finally {
60076
59751
  clearTimeout(timer);
60077
- signal?.removeEventListener("abort", abortFromCaller);
59752
+ base.signal?.removeEventListener("abort", onAbort);
60078
59753
  }
60079
59754
  }
60080
- async backOff(tag, attempt, retriesLeft, reason, headers, { retry, signal }) {
60081
- const delay = retryDelayMs(attempt, headers, retry);
60082
- const nth = attempt + 1;
60083
- const total = attempt + retriesLeft;
60084
- this.logger.info(`${tag} retrying in ${delay}ms (retry ${nth}/${total}) after ${reason}`);
60085
- try {
60086
- await sleep3(delay, signal);
60087
- } catch (err) {
60088
- this.logger.info(`${tag} aborted by caller while waiting to retry`);
60089
- throw new APIUserAbortError(undefined, { cause: err });
60090
- }
59755
+ function baseOf(args) {
59756
+ return {
59757
+ state: args.state,
59758
+ sensitive: args.sensitive,
59759
+ minConfidence: args.minConfidence,
59760
+ signal: args.signal
59761
+ };
60091
59762
  }
60092
- };
60093
- var parseBody = async (res) => {
60094
- const text = await res.text();
60095
- if (text.length === 0)
60096
- return;
60097
- if ((res.headers.get("content-type") ?? "").includes("application/json"))
60098
- try {
60099
- return JSON.parse(text);
60100
- } catch {
60101
- return text;
59763
+ return {
59764
+ enabled: apiKey.length > 0,
59765
+ async choice(args) {
59766
+ const q2 = {
59767
+ type: "choice",
59768
+ question: args.question,
59769
+ options: args.options,
59770
+ fallback: args.fallback
59771
+ };
59772
+ const { result } = await run({ result: q2 }, baseOf(args));
59773
+ return result;
59774
+ },
59775
+ async score(args) {
59776
+ const q2 = {
59777
+ type: "score",
59778
+ question: args.question,
59779
+ levels: args.levels,
59780
+ fallback: args.fallback
59781
+ };
59782
+ const { result } = await run({ result: q2 }, baseOf(args));
59783
+ return result;
59784
+ },
59785
+ async noul(args) {
59786
+ const q2 = { type: "noul", question: args.question, fallback: args.fallback };
59787
+ const { result } = await run({ result: q2 }, baseOf(args));
59788
+ return result;
59789
+ },
59790
+ async batch(args) {
59791
+ return await run(args.questions, baseOf(args));
60102
59792
  }
60103
- try {
60104
- return JSON.parse(text);
60105
- } catch {
60106
- return text;
60107
- }
60108
- };
59793
+ };
59794
+ }
60109
59795
 
60110
59796
  // src/lib/jev-categorize.ts
60111
59797
  var JEV_MODEL = "jev-1.13.0";
@@ -60125,18 +59811,18 @@ function assertJevAllowed(crypto3, env2 = process.env) {
60125
59811
  throw new Error("JEV_API_KEY is not set — Jev categorization is off.");
60126
59812
  return key;
60127
59813
  }
60128
- function makeJevClassifier(apiKey) {
60129
- const client = new TypeSafeClient({ apiKey, timeout: TIMEOUT_MS });
60130
- return async (state, options) => {
60131
- const { answers } = await client.systemOne({
60132
- model: JEV_MODEL,
60133
- state,
60134
- questions: {
60135
- category: choice("Which spending category best fits this bank transaction?", options)
60136
- }
60137
- });
60138
- return { choice: answers.category.choice, confidence: answers.category.confidence };
60139
- };
59814
+ function jevAllowSensitive(crypto3, env2) {
59815
+ return crypto3 === undefined || env2.JEV_ALLOW_PRIVATE_MODE === "1";
59816
+ }
59817
+ function makeJev(apiKey, crypto3, env2 = process.env, fetchImpl) {
59818
+ return createJev({
59819
+ apiKey,
59820
+ model: JEV_MODEL,
59821
+ minConfidence: DEFAULT_MIN_CONFIDENCE,
59822
+ timeoutMs: TIMEOUT_MS,
59823
+ allowSensitive: jevAllowSensitive(crypto3, env2),
59824
+ ...fetchImpl ? { fetch: fetchImpl } : {}
59825
+ });
60140
59826
  }
60141
59827
  var UNSURE = "unsure";
60142
59828
  function optionsFor(type, index) {
@@ -60154,26 +59840,33 @@ function groupState(type, sample) {
60154
59840
  const dir = type === "credit" ? "Money received" : "Money paid out";
60155
59841
  return `${dir}. Bank narration: "${sample.slice(0, 80)}"`;
60156
59842
  }
60157
- async function jevCategorize(client, crypto3, classify, opts = {}) {
59843
+ async function jevCategorize(client, crypto3, jev, opts = {}) {
60158
59844
  assertPrivateModeAllowed(crypto3, opts.env);
60159
- const min = opts.minConfidence ?? DEFAULT_MIN_CONFIDENCE;
60160
59845
  const index = await loadCategoryIndex(client);
60161
59846
  const rows = await fetchUncategorized(client, crypto3, opts);
60162
59847
  const groups = groupRows(rows);
60163
- const unparsed = groups.filter((g3) => g3.pattern === UNPARSED).length;
60164
- const candidates = groups.filter((g3) => g3.pattern !== UNPARSED);
59848
+ const unparsed = groups.filter((g2) => g2.pattern === UNPARSED).length;
59849
+ const candidates = groups.filter((g2) => g2.pattern !== UNPARSED);
60165
59850
  let errors3 = 0;
60166
- const suggestions = await mapLimit(candidates, CONCURRENCY, async (g3) => {
60167
- try {
60168
- const options = optionsFor(g3.type, index);
60169
- const sample = g3.rows[0].description;
60170
- const text = crypto3 ? g3.pattern : sample;
60171
- const r2 = await classify(groupState(g3.type, text), options);
60172
- return r2.choice in options ? { g: g3, ...r2 } : null;
60173
- } catch {
60174
- errors3 += 1;
60175
- return null;
59851
+ const suggestions = await mapLimit(candidates, CONCURRENCY, async (g2) => {
59852
+ const options = optionsFor(g2.type, index);
59853
+ const sample = g2.rows[0].description;
59854
+ const text = crypto3 ? g2.pattern : sample;
59855
+ const d2 = await jev.choice({
59856
+ question: "Which spending category best fits this bank transaction?",
59857
+ options,
59858
+ fallback: UNSURE,
59859
+ state: groupState(g2.type, text),
59860
+ sensitive: true,
59861
+ minConfidence: opts.minConfidence
59862
+ });
59863
+ if (d2.confident)
59864
+ return { g: g2, choice: d2.value, confidence: d2.confidence, confident: true };
59865
+ if (d2.reason === "low_confidence" && d2.answer !== undefined) {
59866
+ return { g: g2, choice: d2.answer, confidence: d2.confidence ?? 0, confident: false };
60176
59867
  }
59868
+ errors3 += 1;
59869
+ return null;
60177
59870
  });
60178
59871
  const accepted = [];
60179
59872
  const review = [];
@@ -60181,7 +59874,7 @@ async function jevCategorize(client, crypto3, classify, opts = {}) {
60181
59874
  if (!s2)
60182
59875
  continue;
60183
59876
  const isTransfer = index.bySlug.get(s2.choice)?.type === "transfer";
60184
- if (s2.choice !== UNSURE && !isTransfer && s2.confidence >= min) {
59877
+ if (s2.confident && s2.choice !== UNSURE && !isTransfer) {
60185
59878
  accepted.push({
60186
59879
  pattern: s2.g.pattern,
60187
59880
  type: s2.g.type,
@@ -60230,7 +59923,8 @@ function jevTools(client, crypto3) {
60230
59923
  }),
60231
59924
  handler: async ({ startDate, endDate, minConfidence, dryRun }) => {
60232
59925
  const key = assertJevAllowed(crypto3);
60233
- const result = await jevCategorize(client, crypto3, makeJevClassifier(key), {
59926
+ const jev = makeJev(key, crypto3);
59927
+ const result = await jevCategorize(client, crypto3, jev, {
60234
59928
  startDate,
60235
59929
  endDate,
60236
59930
  minConfidence,
@@ -60268,15 +59962,7 @@ function settingsTools(client) {
60268
59962
  describe: { name: "", ownerLabels: "Owner slug → display name" },
60269
59963
  variants: { profile: profile.update, household: household.update }
60270
59964
  });
60271
- return [
60272
- get.tool,
60273
- update.tool,
60274
- get.alias("get_profile", "profile"),
60275
- get.alias("get_household", "household"),
60276
- get.alias("get_encryption_status", "encryption"),
60277
- update.alias("update_profile", "profile"),
60278
- update.alias("update_household", "household")
60279
- ];
59965
+ return [get.tool, update.tool];
60280
59966
  }
60281
59967
 
60282
59968
  // src/tools/statement-import.ts
@@ -60367,9 +60053,8 @@ async function runStatementImport(deps, args) {
60367
60053
  throw new Error(`${basename(args.filePath)} parsed to 0 transactions (format ${format}). If it is a statement with real activity, the layout may not be supported.`);
60368
60054
  }
60369
60055
  const dates = txns.map((t2) => t2.date).sort();
60370
- const existing = await fetchReconciliationContext(client, account.id, dates[0], dates[dates.length - 1]);
60371
- if (crypto3)
60372
- await decryptTransactionFields(existing, crypto3.key);
60056
+ const existing0 = await fetchReconciliationContext(client, account.id, dates[0], dates[dates.length - 1]);
60057
+ const existing = crypto3 ? (await decryptTransactionFields(existing0, crypto3.key)).data : existing0;
60373
60058
  const present = existing.map((r2) => ({ ...r2, isDuplicate: false, isIgnored: false }));
60374
60059
  const result = reconcile(present, { ...statement, transactions: txns });
60375
60060
  const fresh = result.unmatched;
@@ -60562,15 +60247,7 @@ function statementImportTools(client, crypto3) {
60562
60247
  })
60563
60248
  }
60564
60249
  });
60565
- return [
60566
- preview.tool,
60567
- commit.tool,
60568
- aliasTool("import_statement_pdf", pdfInput.extend({ confirm: exports_external.boolean().optional() }), "preview_import / import_statement", 'from: "pdf" (confirm:true → import_statement, else preview_import)', ({ confirm, ...args }) => confirm ? [commit.tool, { ...args, from: "pdf" }] : [preview.tool, { ...args, from: "pdf" }]),
60569
- preview.alias("import_csv_parse", "csv"),
60570
- commit.alias("import_csv_confirm", "csv"),
60571
- commit.alias("import_statement_batch", "rows"),
60572
- commit.alias("import_statement_file", "json_file")
60573
- ];
60250
+ return [preview.tool, commit.tool];
60574
60251
  }
60575
60252
 
60576
60253
  // src/registrations.ts
@@ -60715,7 +60392,7 @@ function registerResources(server, tools) {
60715
60392
  // package.json
60716
60393
  var package_default = {
60717
60394
  name: "paisa-mcp",
60718
- version: "0.2.0",
60395
+ version: "0.3.1",
60719
60396
  repository: {
60720
60397
  type: "git",
60721
60398
  url: "git+https://github.com/nimit9/paisa.git",
@@ -60742,13 +60419,14 @@ var package_default = {
60742
60419
  devDependencies: {
60743
60420
  "@modelcontextprotocol/client": "^2.1.0",
60744
60421
  "@modelcontextprotocol/server": "^2.1.0",
60422
+ "@nimit9/signet-ai": "^0.1.11",
60423
+ "@nimit9/signet-lib": "^0.1.15",
60745
60424
  "@nimit9/signet-server": "^0.2.2",
60746
60425
  "@paisa/parsers": "workspace:*",
60747
60426
  "@paisa/reconciliation": "workspace:*",
60748
60427
  "@paisa/types": "workspace:*",
60749
60428
  "@scure/bip39": "^2.2.0",
60750
60429
  "@types/bun": "^1.3.14",
60751
- "@typesafe-ai/sdk": "^0.6.0",
60752
60430
  axios: "^1.16.1",
60753
60431
  "hash-wasm": "^4.12.0",
60754
60432
  typescript: "^7.0.2",
@@ -60760,7 +60438,7 @@ var package_default = {
60760
60438
  };
60761
60439
 
60762
60440
  // src/version.ts
60763
- var VERSION3 = package_default.version;
60441
+ var VERSION2 = package_default.version;
60764
60442
 
60765
60443
  // src/server.ts
60766
60444
  function paisaErrorResult(err) {
@@ -60772,31 +60450,18 @@ function paisaErrorResult(err) {
60772
60450
  function buildServer(client, crypto3) {
60773
60451
  const tools = applyAnnotations(REGISTRATIONS.flatMap((group) => group.tools(client, crypto3)));
60774
60452
  for (const t2 of tools)
60775
- if (t2.input && !isAlias(t2))
60453
+ if (t2.input)
60776
60454
  withCompactJsonSchema(t2.input);
60777
60455
  const server = createMcpServer({
60778
60456
  name: "paisa",
60779
- version: VERSION3,
60457
+ version: VERSION2,
60780
60458
  tools,
60781
60459
  errorResult: paisaErrorResult
60782
60460
  });
60783
- hideFromToolsList(server, new Set(tools.filter(isAlias).map((t2) => t2.name)));
60784
60461
  registerResources(server, tools);
60785
60462
  registerPrompts(server);
60786
60463
  return server;
60787
60464
  }
60788
- function hideFromToolsList(server, hidden) {
60789
- if (hidden.size === 0)
60790
- return;
60791
- const inner = server.server;
60792
- const list = inner._getRequestHandler("tools/list");
60793
- if (!list)
60794
- throw new Error("MCP SDK: no tools/list handler to wrap (aliases would be listed)");
60795
- server.server.setRequestHandler("tools/list", async (request, ctx) => {
60796
- const result = await list(request, ctx);
60797
- return { ...result, tools: result.tools.filter((t2) => !hidden.has(t2.name)) };
60798
- });
60799
- }
60800
60465
 
60801
60466
  // src/index.ts
60802
60467
  async function main() {
@@ -60819,5 +60484,5 @@ main().catch((err) => {
60819
60484
  process.exit(1);
60820
60485
  });
60821
60486
 
60822
- //# debugId=1E3042577A7AFC5F64756E2164756E21
60487
+ //# debugId=E169545C1C52BE7864756E2164756E21
60823
60488
  //# sourceMappingURL=index.js.map