paisa-mcp 0.1.0 → 0.3.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/dist/index.js CHANGED
@@ -39147,6 +39147,220 @@ function validateAndWarnToolName(name) {
39147
39147
  issueToolNameWarning(name, result.warnings);
39148
39148
  return result.isValid;
39149
39149
  }
39150
+ var MAX_TEMPLATE_LENGTH = 1e6;
39151
+ var MAX_VARIABLE_LENGTH = 1e6;
39152
+ var MAX_TEMPLATE_EXPRESSIONS = 1e4;
39153
+ var MAX_REGEX_LENGTH = 1e6;
39154
+ var UriTemplate = class UriTemplate2 {
39155
+ static isTemplate(str) {
39156
+ return /\{[^}\s]+\}/.test(str);
39157
+ }
39158
+ static validateLength(str, max, context) {
39159
+ if (str.length > max)
39160
+ throw new Error(`${context} exceeds maximum length of ${max} characters (got ${str.length})`);
39161
+ }
39162
+ template;
39163
+ parts;
39164
+ get variableNames() {
39165
+ return this.parts.flatMap((part) => typeof part === "string" ? [] : part.names);
39166
+ }
39167
+ constructor(template) {
39168
+ UriTemplate2.validateLength(template, MAX_TEMPLATE_LENGTH, "Template");
39169
+ this.template = template;
39170
+ this.parts = this.parse(template);
39171
+ }
39172
+ toString() {
39173
+ return this.template;
39174
+ }
39175
+ parse(template) {
39176
+ const parts = [];
39177
+ let currentText = "";
39178
+ let i = 0;
39179
+ let expressionCount = 0;
39180
+ while (i < template.length)
39181
+ if (template[i] === "{") {
39182
+ if (currentText) {
39183
+ parts.push(currentText);
39184
+ currentText = "";
39185
+ }
39186
+ const end = template.indexOf("}", i);
39187
+ if (end === -1)
39188
+ throw new Error("Unclosed template expression");
39189
+ expressionCount++;
39190
+ if (expressionCount > MAX_TEMPLATE_EXPRESSIONS)
39191
+ throw new Error(`Template contains too many expressions (max ${MAX_TEMPLATE_EXPRESSIONS})`);
39192
+ const expr = template.slice(i + 1, end);
39193
+ const operator = this.getOperator(expr);
39194
+ const exploded = expr.includes("*");
39195
+ const names = this.getNames(expr);
39196
+ const name = names[0];
39197
+ for (const name$1 of names)
39198
+ UriTemplate2.validateLength(name$1, MAX_VARIABLE_LENGTH, "Variable name");
39199
+ parts.push({
39200
+ name,
39201
+ operator,
39202
+ names,
39203
+ exploded
39204
+ });
39205
+ i = end + 1;
39206
+ } else {
39207
+ currentText += template[i];
39208
+ i++;
39209
+ }
39210
+ if (currentText)
39211
+ parts.push(currentText);
39212
+ return parts;
39213
+ }
39214
+ getOperator(expr) {
39215
+ return [
39216
+ "+",
39217
+ "#",
39218
+ ".",
39219
+ "/",
39220
+ "?",
39221
+ "&"
39222
+ ].find((op) => expr.startsWith(op)) || "";
39223
+ }
39224
+ getNames(expr) {
39225
+ const operator = this.getOperator(expr);
39226
+ return expr.slice(operator.length).split(",").map((name) => name.replace("*", "").trim()).filter((name) => name.length > 0);
39227
+ }
39228
+ encodeValue(value, operator) {
39229
+ UriTemplate2.validateLength(value, MAX_VARIABLE_LENGTH, "Variable value");
39230
+ if (operator === "+" || operator === "#")
39231
+ return encodeURI(value);
39232
+ return encodeURIComponent(value);
39233
+ }
39234
+ expandPart(part, variables) {
39235
+ if (part.operator === "?" || part.operator === "&") {
39236
+ const pairs = part.names.map((name) => {
39237
+ const value$1 = variables[name];
39238
+ if (value$1 === undefined)
39239
+ return "";
39240
+ return `${name}=${Array.isArray(value$1) ? value$1.map((v) => this.encodeValue(v, part.operator)).join(",") : this.encodeValue(value$1.toString(), part.operator)}`;
39241
+ }).filter((pair) => pair.length > 0);
39242
+ if (pairs.length === 0)
39243
+ return "";
39244
+ return (part.operator === "?" ? "?" : "&") + pairs.join("&");
39245
+ }
39246
+ if (part.names.length > 1) {
39247
+ const values = part.names.map((name) => variables[name]).filter((v) => v !== undefined);
39248
+ if (values.length === 0)
39249
+ return "";
39250
+ return values.map((v) => Array.isArray(v) ? v[0] : v).join(",");
39251
+ }
39252
+ const value = variables[part.name];
39253
+ if (value === undefined)
39254
+ return "";
39255
+ const encoded = (Array.isArray(value) ? value : [value]).map((v) => this.encodeValue(v, part.operator));
39256
+ switch (part.operator) {
39257
+ case "":
39258
+ return encoded.join(",");
39259
+ case "+":
39260
+ return encoded.join(",");
39261
+ case "#":
39262
+ return "#" + encoded.join(",");
39263
+ case ".":
39264
+ return "." + encoded.join(".");
39265
+ case "/":
39266
+ return "/" + encoded.join("/");
39267
+ default:
39268
+ return encoded.join(",");
39269
+ }
39270
+ }
39271
+ expand(variables) {
39272
+ let result = "";
39273
+ let hasQueryParam = false;
39274
+ for (const part of this.parts) {
39275
+ if (typeof part === "string") {
39276
+ result += part;
39277
+ continue;
39278
+ }
39279
+ const expanded = this.expandPart(part, variables);
39280
+ if (!expanded)
39281
+ continue;
39282
+ result += (part.operator === "?" || part.operator === "&") && hasQueryParam ? expanded.replace("?", "&") : expanded;
39283
+ if (part.operator === "?" || part.operator === "&")
39284
+ hasQueryParam = true;
39285
+ }
39286
+ return result;
39287
+ }
39288
+ escapeRegExp(str) {
39289
+ return str.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
39290
+ }
39291
+ partToRegExp(part) {
39292
+ const patterns = [];
39293
+ for (const name$1 of part.names)
39294
+ UriTemplate2.validateLength(name$1, MAX_VARIABLE_LENGTH, "Variable name");
39295
+ if (part.operator === "?" || part.operator === "&") {
39296
+ for (let i = 0;i < part.names.length; i++) {
39297
+ const name$1 = part.names[i];
39298
+ const prefix = i === 0 ? "\\" + part.operator : "&";
39299
+ patterns.push({
39300
+ pattern: prefix + this.escapeRegExp(name$1) + "=([^&]+)",
39301
+ name: name$1
39302
+ });
39303
+ }
39304
+ return patterns;
39305
+ }
39306
+ let pattern;
39307
+ const name = part.name;
39308
+ switch (part.operator) {
39309
+ case "":
39310
+ pattern = part.exploded ? "([^/,]+(?:,[^/,]+)*)" : "([^/,]+)";
39311
+ break;
39312
+ case "+":
39313
+ case "#":
39314
+ pattern = "(.+)";
39315
+ break;
39316
+ case ".":
39317
+ pattern = String.raw`\.([^/,]+)`;
39318
+ break;
39319
+ case "/":
39320
+ pattern = "/" + (part.exploded ? "([^/,]+(?:,[^/,]+)*)" : "([^/,]+)");
39321
+ break;
39322
+ default:
39323
+ pattern = "([^/]+)";
39324
+ }
39325
+ patterns.push({
39326
+ pattern,
39327
+ name
39328
+ });
39329
+ return patterns;
39330
+ }
39331
+ match(uri) {
39332
+ UriTemplate2.validateLength(uri, MAX_TEMPLATE_LENGTH, "URI");
39333
+ let pattern = "^";
39334
+ const names = [];
39335
+ for (const part of this.parts)
39336
+ if (typeof part === "string")
39337
+ pattern += this.escapeRegExp(part);
39338
+ else {
39339
+ const patterns = this.partToRegExp(part);
39340
+ for (const { pattern: partPattern2, name } of patterns) {
39341
+ pattern += partPattern2;
39342
+ names.push({
39343
+ name,
39344
+ exploded: part.exploded
39345
+ });
39346
+ }
39347
+ }
39348
+ pattern += "$";
39349
+ UriTemplate2.validateLength(pattern, MAX_REGEX_LENGTH, "Generated regex pattern");
39350
+ const regex = new RegExp(pattern);
39351
+ const match = uri.match(regex);
39352
+ if (!match)
39353
+ return null;
39354
+ const result = {};
39355
+ for (const [i, name_] of names.entries()) {
39356
+ const { name, exploded } = name_;
39357
+ const value = match[i + 1];
39358
+ const cleanName = name.replace("*", "");
39359
+ result[cleanName] = exploded && value.includes(",") ? value.split(",") : value;
39360
+ }
39361
+ return result;
39362
+ }
39363
+ };
39150
39364
  function isZodV4Schema(v) {
39151
39365
  return typeof v === "object" && v !== null && "_zod" in v;
39152
39366
  }
@@ -48100,6 +48314,22 @@ var McpServer = class {
48100
48314
  this.server.sendPromptListChanged();
48101
48315
  }
48102
48316
  };
48317
+ var ResourceTemplate = class {
48318
+ _uriTemplate;
48319
+ constructor(uriTemplate, _callbacks) {
48320
+ this._callbacks = _callbacks;
48321
+ this._uriTemplate = typeof uriTemplate === "string" ? new UriTemplate(uriTemplate) : uriTemplate;
48322
+ }
48323
+ get uriTemplate() {
48324
+ return this._uriTemplate;
48325
+ }
48326
+ get listCallback() {
48327
+ return this._callbacks.list;
48328
+ }
48329
+ completeCallback(variable) {
48330
+ return this._callbacks.complete?.[variable];
48331
+ }
48332
+ };
48103
48333
  function createToolExecutor(inputSchema, handler) {
48104
48334
  if (inputSchema) {
48105
48335
  const callback$1 = handler;
@@ -53070,7 +53300,7 @@ async function initCrypto(client, pin, userId) {
53070
53300
  // ../../node_modules/.bun/@modelcontextprotocol+server@2.1.0/node_modules/@modelcontextprotocol/server/dist/index.mjs
53071
53301
  var DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024 * 1024;
53072
53302
 
53073
- // ../../node_modules/.bun/@nimit9+signet-server@0.2.2+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/http/status-codes.js
53303
+ // ../../node_modules/.bun/@nimit9+signet-server@0.2.3+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/http/status-codes.js
53074
53304
  var STATUS_CODES = {
53075
53305
  400: "BAD_REQUEST",
53076
53306
  401: "UNAUTHORIZED",
@@ -53084,7 +53314,7 @@ var STATUS_CODES = {
53084
53314
  429: "RATE_LIMITED"
53085
53315
  };
53086
53316
 
53087
- // ../../node_modules/.bun/@nimit9+signet-server@0.2.2+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/mcp/server.js
53317
+ // ../../node_modules/.bun/@nimit9+signet-server@0.2.3+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/mcp/server.js
53088
53318
  var RAW = Symbol.for("signet.mcp.rawResult");
53089
53319
  function toolResult(result) {
53090
53320
  return Object.defineProperty({ ...result }, RAW, { value: true, enumerable: false });
@@ -53168,10 +53398,17 @@ function toResult(value, structured) {
53168
53398
  return { content: [{ type: "text", text }] };
53169
53399
  }
53170
53400
  function createMcpServer(options) {
53401
+ const unlisted = new Set(options.tools.filter((t) => t.listed === false).map((t) => t.name));
53402
+ const toolsCapability = { tools: { listChanged: false } };
53171
53403
  const server = new McpServer({ name: options.name, version: options.version }, {
53172
- capabilities: { tools: { listChanged: false } },
53404
+ ...unlisted.size ? {} : { capabilities: toolsCapability },
53173
53405
  ...options.instructions ? { instructions: options.instructions } : {}
53174
53406
  });
53407
+ let restore;
53408
+ if (unlisted.size) {
53409
+ server.server.registerCapabilities(toolsCapability);
53410
+ restore = hideUnlisted(server, unlisted);
53411
+ }
53175
53412
  const register2 = server.registerTool.bind(server);
53176
53413
  const { c, token } = options.context ?? {};
53177
53414
  for (const tool of options.tools) {
@@ -53215,29 +53452,110 @@ function createMcpServer(options) {
53215
53452
  ...tool.input ? { inputSchema: tool.input } : {},
53216
53453
  ...tool.output ? { outputSchema: tool.output } : {}
53217
53454
  }, tool.input ? (input2, mcp) => run(input2, mcp) : (mcp) => run({}, mcp));
53455
+ restore?.();
53456
+ restore = undefined;
53218
53457
  }
53219
53458
  return server;
53220
53459
  }
53460
+ function hideUnlisted(server, unlisted) {
53461
+ const inner = server.server;
53462
+ const original = inner.setRequestHandler.bind(inner);
53463
+ let caught = false;
53464
+ inner.setRequestHandler = (method, handler) => {
53465
+ if (method !== "tools/list" || typeof handler !== "function")
53466
+ return original(method, handler);
53467
+ caught = true;
53468
+ original(method, async (request, ctx) => {
53469
+ const result = await handler(request, ctx);
53470
+ return { ...result, tools: result.tools.filter((t) => !unlisted.has(t.name)) };
53471
+ });
53472
+ };
53473
+ return () => {
53474
+ delete inner.setRequestHandler;
53475
+ if (!caught)
53476
+ throw new Error("signet mcp: could not hide unlisted tools; @modelcontextprotocol/server no longer installs tools/list through setRequestHandler");
53477
+ };
53478
+ }
53479
+
53480
+ // src/annotations.ts
53481
+ var RO = { readOnlyHint: true, openWorldHint: false };
53482
+ var WI = {
53483
+ readOnlyHint: false,
53484
+ destructiveHint: false,
53485
+ idempotentHint: true,
53486
+ openWorldHint: false
53487
+ };
53488
+ var W = {
53489
+ readOnlyHint: false,
53490
+ destructiveHint: false,
53491
+ idempotentHint: false,
53492
+ openWorldHint: false
53493
+ };
53494
+ var D = {
53495
+ readOnlyHint: false,
53496
+ destructiveHint: true,
53497
+ idempotentHint: false,
53498
+ openWorldHint: false
53499
+ };
53500
+ var WI_OPEN = {
53501
+ readOnlyHint: false,
53502
+ destructiveHint: false,
53503
+ idempotentHint: true,
53504
+ openWorldHint: true
53505
+ };
53506
+ var TOOL_META = {
53507
+ get_transactions: { title: "Search Transactions", annotations: RO },
53508
+ get_uncategorized_groups: { title: "Group Uncategorized Transactions", annotations: RO },
53509
+ categorize_groups: { title: "Categorize Transaction Groups", annotations: W },
53510
+ backfill_persons: { title: "Backfill Persons from Transactions", annotations: WI },
53511
+ update_transaction: { title: "Update Transaction", annotations: WI },
53512
+ bulk_link_entity: { title: "Bulk Link Transactions", annotations: WI },
53513
+ manage_duplicates: { title: "Manage Duplicate Transactions", annotations: W },
53514
+ get_reconciliation_context: { title: "Get Reconciliation Context", annotations: RO },
53515
+ bulk_categorize_transactions: { title: "Bulk Categorize Transactions", annotations: WI },
53516
+ delete_transactions: { title: "Delete Transactions", annotations: D },
53517
+ rescan_transactions: { title: "Rescan Transactions", annotations: WI },
53518
+ get_settlement_candidates: { title: "Find Settlement Candidates", annotations: RO },
53519
+ get_transaction: { title: "Get Transaction", annotations: RO },
53520
+ preview_import: { title: "Preview Statement Import", annotations: RO },
53521
+ import_statement: { title: "Import Statement", annotations: W },
53522
+ auto_categorize_jev: { title: "Auto-Categorize with Jev", annotations: W },
53523
+ list_entities: { title: "List Entities", annotations: RO },
53524
+ upsert_entity: { title: "Create or Update Entity", annotations: W },
53525
+ delete_entity: { title: "Delete Entity", annotations: D },
53526
+ merge_merchants: { title: "Merge Merchants", annotations: D },
53527
+ get_analytics: { title: "Get Analytics", annotations: RO },
53528
+ upsert_savings: { title: "Create or Update Savings Vehicle", annotations: W },
53529
+ contribute_to_goal: { title: "Contribute to Goal", annotations: W },
53530
+ set_budget: { title: "Set Budget", annotations: WI },
53531
+ get_dashboard: { title: "Get Dashboard", annotations: RO },
53532
+ sync_recurring: { title: "Sync Recurring Merchants", annotations: WI },
53533
+ check_alerts: { title: "Check Alerts", annotations: W },
53534
+ manage_alerts: { title: "Manage Alerts and Rules", annotations: WI },
53535
+ manage_debt: { title: "Add or Settle Debt", annotations: W },
53536
+ generate_monthly_report: { title: "Generate Monthly Report", annotations: WI },
53537
+ get_report: { title: "Get Report", annotations: RO },
53538
+ export_transactions: { title: "Export Transactions", annotations: RO },
53539
+ set_learning_rule: { title: "Set Learning Rule", annotations: WI },
53540
+ update_net_worth: { title: "Update Net Worth", annotations: WI },
53541
+ upsert_investment: { title: "Create or Update Investment", annotations: W },
53542
+ sync_zerodha: { title: "Sync Zerodha", annotations: WI_OPEN },
53543
+ get_settings: { title: "Get Settings", annotations: RO },
53544
+ update_settings: { title: "Update Settings", annotations: WI },
53545
+ set_encryption_mode: { title: "Set Encryption Mode", annotations: D }
53546
+ };
53547
+ function applyAnnotations(tools) {
53548
+ return tools.map((tool) => {
53549
+ const meta3 = TOOL_META[tool.name];
53550
+ if (!meta3)
53551
+ throw new Error(`No TOOL_META entry for tool "${tool.name}" — add one to annotations.ts`);
53552
+ return { ...tool, title: meta3.title, annotations: meta3.annotations };
53553
+ });
53554
+ }
53221
53555
  // src/lib/union-tool.ts
53222
53556
  function variant(input2, run) {
53223
53557
  return { input: input2, run };
53224
53558
  }
53225
- function isAlias(tool) {
53226
- return typeof tool.aliasFor === "string";
53227
- }
53228
- function aliasTool(name, input2, aliasFor, note, route) {
53229
- const tool = defineTool({
53230
- name,
53231
- description: `Deprecated alias of ${aliasFor}.`,
53232
- input: input2,
53233
- handler: async (args, ctx) => {
53234
- const [to, next] = route(args);
53235
- const parsed = to.input ? await to.input.parseAsync(next) : next;
53236
- return to.handler(parsed, ctx);
53237
- }
53238
- });
53239
- return { ...tool, aliasFor, aliasNote: note };
53240
- }
53241
53559
  function compactJsonSchema(schema) {
53242
53560
  const walk = (node2, root) => {
53243
53561
  if (Array.isArray(node2))
@@ -53334,91 +53652,109 @@ function unionTool(opts) {
53334
53652
  return v.run(rest, ctx);
53335
53653
  }
53336
53654
  });
53337
- return {
53338
- tool,
53339
- alias: (aliasName, value) => aliasTool(aliasName, variants[value].input, name, `${key}: "${value}"`, (a) => [
53340
- tool,
53341
- { ...a, [key]: value }
53342
- ])
53343
- };
53655
+ return { tool };
53344
53656
  }
53345
53657
 
53346
- // src/annotations.ts
53347
- var RO = { readOnlyHint: true, openWorldHint: false };
53348
- var WI = {
53349
- readOnlyHint: false,
53350
- destructiveHint: false,
53351
- idempotentHint: true,
53352
- openWorldHint: false
53353
- };
53354
- var W = {
53355
- readOnlyHint: false,
53356
- destructiveHint: false,
53357
- idempotentHint: false,
53358
- openWorldHint: false
53359
- };
53360
- var D = {
53361
- readOnlyHint: false,
53362
- destructiveHint: true,
53363
- idempotentHint: false,
53364
- openWorldHint: false
53365
- };
53366
- var WI_OPEN = {
53367
- readOnlyHint: false,
53368
- destructiveHint: false,
53369
- idempotentHint: true,
53370
- openWorldHint: true
53371
- };
53372
- var TOOL_META = {
53373
- get_transactions: { title: "Search Transactions", annotations: RO },
53374
- get_uncategorized_groups: { title: "Group Uncategorized Transactions", annotations: RO },
53375
- categorize_groups: { title: "Categorize Transaction Groups", annotations: W },
53376
- backfill_persons: { title: "Backfill Persons from Transactions", annotations: WI },
53377
- update_transaction: { title: "Update Transaction", annotations: WI },
53378
- bulk_link_entity: { title: "Bulk Link Transactions", annotations: WI },
53379
- manage_duplicates: { title: "Manage Duplicate Transactions", annotations: W },
53380
- get_reconciliation_context: { title: "Get Reconciliation Context", annotations: RO },
53381
- bulk_categorize_transactions: { title: "Bulk Categorize Transactions", annotations: WI },
53382
- delete_transactions: { title: "Delete Transactions", annotations: D },
53383
- rescan_transactions: { title: "Rescan Transactions", annotations: WI },
53384
- get_settlement_candidates: { title: "Find Settlement Candidates", annotations: RO },
53385
- get_transaction: { title: "Get Transaction", annotations: RO },
53386
- preview_import: { title: "Preview Statement Import", annotations: RO },
53387
- import_statement: { title: "Import Statement", annotations: W },
53388
- auto_categorize_jev: { title: "Auto-Categorize with Jev", annotations: W },
53389
- list_entities: { title: "List Entities", annotations: RO },
53390
- upsert_entity: { title: "Create or Update Entity", annotations: W },
53391
- delete_entity: { title: "Delete Entity", annotations: D },
53392
- merge_merchants: { title: "Merge Merchants", annotations: D },
53393
- get_analytics: { title: "Get Analytics", annotations: RO },
53394
- upsert_savings: { title: "Create or Update Savings Vehicle", annotations: W },
53395
- contribute_to_goal: { title: "Contribute to Goal", annotations: W },
53396
- set_budget: { title: "Set Budget", annotations: WI },
53397
- get_dashboard: { title: "Get Dashboard", annotations: RO },
53398
- sync_recurring: { title: "Sync Recurring Merchants", annotations: WI },
53399
- check_alerts: { title: "Check Alerts", annotations: W },
53400
- manage_alerts: { title: "Manage Alerts and Rules", annotations: WI },
53401
- manage_debt: { title: "Add or Settle Debt", annotations: W },
53402
- generate_monthly_report: { title: "Generate Monthly Report", annotations: WI },
53403
- get_report: { title: "Get Report", annotations: RO },
53404
- export_transactions: { title: "Export Transactions", annotations: RO },
53405
- set_learning_rule: { title: "Set Learning Rule", annotations: WI },
53406
- update_net_worth: { title: "Update Net Worth", annotations: WI },
53407
- upsert_investment: { title: "Create or Update Investment", annotations: W },
53408
- sync_zerodha: { title: "Sync Zerodha", annotations: WI_OPEN },
53409
- get_settings: { title: "Get Settings", annotations: RO },
53410
- update_settings: { title: "Update Settings", annotations: WI },
53411
- set_encryption_mode: { title: "Set Encryption Mode", annotations: D }
53412
- };
53413
- function applyAnnotations(tools) {
53414
- return tools.map((tool) => {
53415
- if (isAlias(tool))
53416
- return tool;
53417
- const meta3 = TOOL_META[tool.name];
53418
- if (!meta3)
53419
- throw new Error(`No TOOL_META entry for tool "${tool.name}" — add one to annotations.ts`);
53420
- return { ...tool, title: meta3.title, annotations: meta3.annotations };
53421
- });
53658
+ // src/prompts.ts
53659
+ var CONFIRM = "Ask me before any write; show what will change first (counts, not a dump).";
53660
+ var month = exports_external.string().regex(/^\d{4}-\d{2}$/).optional().describe("YYYY-MM; defaults to the last full month");
53661
+ function monthText(m) {
53662
+ if (!m)
53663
+ return "the last full month";
53664
+ const [y, mm] = m.split("-");
53665
+ return `${m} (month=${Number(mm)}, year=${y})`;
53666
+ }
53667
+ function rangeText(a) {
53668
+ if (a.startDate && a.endDate)
53669
+ return ` startDate=${a.startDate} endDate=${a.endDate}`;
53670
+ if (a.startDate)
53671
+ return ` startDate=${a.startDate}`;
53672
+ if (a.endDate)
53673
+ return ` endDate=${a.endDate}`;
53674
+ return "";
53675
+ }
53676
+ var PROMPTS = [
53677
+ {
53678
+ name: "monthly_review",
53679
+ title: "Monthly review",
53680
+ description: "Review one month: income, spending, savings rate, budgets, what's due next.",
53681
+ args: exports_external.object({ month }),
53682
+ text: (a) => [
53683
+ `Review my finances for ${monthText(a.month)}.`,
53684
+ "1. `check_alerts` — note anything urgent.",
53685
+ "2. `get_report` report='monthly' months=3 — income, expenses, savings rate vs the months before.",
53686
+ "3. `get_analytics` report='spending_trends' months=3 — category breakdown and budget vs actual (read that month's row).",
53687
+ "4. `get_transactions` for that month with amountMin to find the biggest items (limit 25).",
53688
+ "5. `get_report` report='upcoming' days=30 — EMIs, SIPs, renewals, card bills.",
53689
+ "Summarize: income, spend, savings rate, top categories, over-budget categories, unusual items, upcoming dues. Do not edit, import or delete anything.",
53690
+ "If I ask to save it, call `generate_monthly_report` with month and year."
53691
+ ].join(`
53692
+ `)
53693
+ },
53694
+ {
53695
+ name: "import_statement",
53696
+ title: "Import a statement",
53697
+ description: "Preview, confirm, import and categorize a bank or card statement (PDF or CSV).",
53698
+ args: exports_external.object({
53699
+ filePath: exports_external.string().optional().describe("Local path of the statement file")
53700
+ }),
53701
+ text: (a) => [
53702
+ `Import the statement ${a.filePath ? `at ${a.filePath}` : "I give you (ask for the file path)"}.`,
53703
+ "1. `list_entities` entity='bank_accounts' — pick the account. Missing? Create it with `upsert_entity` entity='bank_account' only after I confirm name, bank, type, last4 and owner.",
53704
+ "2. `preview_import` — from='pdf' with filePath + account, or from='csv' with filePath. It writes nothing. Show me the new / already-in-Paisa / conflict counts and totals (CSV: the detected mapping).",
53705
+ "3. After I confirm, `import_statement` with the same args (CSV: the confirmed mapping). Never pass force unless I say so. CSV import is not available in private mode.",
53706
+ "4. Credit card statement: `get_settlement_candidates` with ccAccountId, then mark the matching bank debits isTransfer=true with `update_transaction` once I confirm.",
53707
+ "5. `sync_recurring`, then categorize what's left with `get_uncategorized_groups` → `categorize_groups` (see the categorize_uncategorized prompt).",
53708
+ CONFIRM
53709
+ ].join(`
53710
+ `)
53711
+ },
53712
+ {
53713
+ name: "categorize_uncategorized",
53714
+ title: "Categorize uncategorized",
53715
+ description: "Group uncategorized transactions, propose categories, apply them and save learning rules.",
53716
+ args: exports_external.object({
53717
+ startDate: exports_external.string().optional().describe("YYYY-MM-DD"),
53718
+ endDate: exports_external.string().optional().describe("YYYY-MM-DD")
53719
+ }),
53720
+ text: (a) => [
53721
+ "Categorize my uncategorized transactions.",
53722
+ "1. `list_entities` entity='categories' (or the paisa://categories resource) for valid slugs.",
53723
+ `2. \`get_uncategorized_groups\`${rangeText(a)} — groups by pattern, biggest first.`,
53724
+ "3. Propose a category slug per group as a table (pattern, count, total, slug). Ask about groups you are unsure of instead of guessing.",
53725
+ `4. After I confirm, \`categorize_groups\`${rangeText(a)} with dryRun=true, show the counts, then run it again without dryRun. It also saves learning rules for future imports.`,
53726
+ "5. Transfers to/from individuals: `backfill_persons` with dryRun=true, then for real once I confirm.",
53727
+ "6. Tell me how many uncategorized groups remain.",
53728
+ CONFIRM
53729
+ ].join(`
53730
+ `)
53731
+ },
53732
+ {
53733
+ name: "budget_check",
53734
+ title: "Budget check",
53735
+ description: "Compare one month's spending to its budgets and suggest changes.",
53736
+ args: exports_external.object({ month }),
53737
+ text: (a) => [
53738
+ `Check my budgets for ${monthText(a.month)}.`,
53739
+ "1. `list_entities` entity='budgets' with month and year.",
53740
+ "2. `get_analytics` report='spending_trends' months=3 — budget vs actual (read that month's row).",
53741
+ "3. Show a table: category, budget, spent, remaining, % used. Flag categories over 80% and over 100%, and big categories with no budget.",
53742
+ "4. Suggest changes. Call `set_budget` (categoryId from `list_entities` entity='categories') only for the changes I approve."
53743
+ ].join(`
53744
+ `)
53745
+ }
53746
+ ];
53747
+ function registerPrompts(server) {
53748
+ for (const p of PROMPTS) {
53749
+ const cb = (args) => ({
53750
+ messages: [{ role: "user", content: { type: "text", text: p.text(args) } }]
53751
+ });
53752
+ if (p.args) {
53753
+ server.registerPrompt(p.name, { title: p.title, description: p.description, argsSchema: p.args }, (args) => cb(args));
53754
+ } else {
53755
+ server.registerPrompt(p.name, { title: p.title, description: p.description }, () => cb({}));
53756
+ }
53757
+ }
53422
53758
  }
53423
53759
 
53424
53760
  // src/tools/alert-rules.ts
@@ -53477,11 +53813,7 @@ function alertTools(client) {
53477
53813
  return { success: true, generated, unread };
53478
53814
  }
53479
53815
  }),
53480
- manage.tool,
53481
- manage.alias("dismiss_alert", "dismiss"),
53482
- manage.alias("mark_alert_read", "read"),
53483
- manage.alias("list_alert_rules", "list_rules"),
53484
- manage.alias("set_alert_rule", "set_rule")
53816
+ manage.tool
53485
53817
  ];
53486
53818
  }
53487
53819
 
@@ -53810,7 +54142,7 @@ function debtTools(client) {
53810
54142
  })
53811
54143
  }
53812
54144
  });
53813
- return [manage.tool, manage.alias("add_debt", "add"), manage.alias("settle_debt", "settle")];
54145
+ return [manage.tool];
53814
54146
  }
53815
54147
 
53816
54148
  // src/tools/accounts.ts
@@ -54571,11 +54903,7 @@ function reportTools(client) {
54571
54903
  }),
54572
54904
  handler: (body) => client.post("/api/reports/generate", body)
54573
54905
  }),
54574
- report2.tool,
54575
- report2.alias("get_reports", "monthly"),
54576
- report2.alias("get_net_worth", "net_worth"),
54577
- report2.alias("get_upcoming", "upcoming"),
54578
- report2.alias("get_spending_heatmap", "heatmap")
54906
+ report2.tool
54579
54907
  ];
54580
54908
  }
54581
54909
 
@@ -54654,56 +54982,7 @@ function entityTools(client, crypto3) {
54654
54982
  fixed_deposit: investment.deleteFd
54655
54983
  }
54656
54984
  });
54657
- return [
54658
- list.tool,
54659
- upsert.tool,
54660
- del.tool,
54661
- list.alias("list_categories", "categories"),
54662
- list.alias("list_merchants", "merchants"),
54663
- list.alias("list_bank_accounts", "bank_accounts"),
54664
- list.alias("list_persons", "persons"),
54665
- list.alias("list_budgets", "budgets"),
54666
- list.alias("list_debts", "debts"),
54667
- list.alias("list_invites", "invites"),
54668
- list.alias("list_alerts", "alerts"),
54669
- list.alias("list_recurring", "recurring"),
54670
- list.alias("list_push_subscriptions", "push_subscriptions"),
54671
- list.alias("list_saved_reports", "saved_reports"),
54672
- 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 }]),
54673
- aliasTool("list_savings", exports_external.object({
54674
- type: exports_external.enum(["goals", "emis", "sinking_funds", "insurance", "contributions"]),
54675
- id: exports_external.string().optional()
54676
- }), "list_entities", "entity: <type> (contributions → goal_contributions)", ({ type, id }) => {
54677
- if (type !== "contributions")
54678
- return [list.tool, { entity: type }];
54679
- if (!id)
54680
- throw new Error("list_savings type='contributions' requires id (goal UUID)");
54681
- return [list.tool, { entity: "goal_contributions", id }];
54682
- }),
54683
- aliasTool("list_investments", exports_external.object({
54684
- type: exports_external.enum(["holdings", "sips", "fixed_deposits", "transactions"]),
54685
- id: exports_external.string().optional()
54686
- }), "list_entities", "entity: <type> (transactions → investment_transactions)", ({ type, id }) => {
54687
- if (type !== "transactions")
54688
- return [list.tool, { entity: type }];
54689
- if (!id)
54690
- throw new Error("list_investments type='transactions' requires id");
54691
- return [list.tool, { entity: "investment_transactions", id }];
54692
- }),
54693
- upsert.alias("upsert_merchant", "merchant"),
54694
- upsert.alias("upsert_person", "person"),
54695
- upsert.alias("upsert_category", "category"),
54696
- upsert.alias("upsert_bank_account", "bank_account"),
54697
- upsert.alias("upsert_insurance", "insurance"),
54698
- upsert.alias("create_invite", "invite"),
54699
- del.alias("delete_category", "category"),
54700
- del.alias("delete_merchant", "merchant"),
54701
- del.alias("delete_person", "person"),
54702
- del.alias("delete_debt", "debt"),
54703
- del.alias("revoke_invite", "invite"),
54704
- 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 }]),
54705
- 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 }])
54706
- ];
54985
+ return [list.tool, upsert.tool, del.tool];
54707
54986
  }
54708
54987
 
54709
54988
  // ../reconciliation/src/aliases.ts
@@ -57279,22 +57558,22 @@ function parseMonthDayYear(raw) {
57279
57558
  const match = raw.trim().match(/^(\w+)\s+(\d{1,2}),?\s+(\d{4})$/);
57280
57559
  if (!match)
57281
57560
  return null;
57282
- const month = MONTH_MAP[match[1].toLowerCase()];
57283
- if (!month)
57561
+ const month2 = MONTH_MAP[match[1].toLowerCase()];
57562
+ if (!month2)
57284
57563
  return null;
57285
57564
  const day = parseInt(match[2], 10);
57286
57565
  const year = parseInt(match[3], 10);
57287
- const iso = `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
57288
- return { month, day, year, iso };
57566
+ const iso = `${year}-${String(month2).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
57567
+ return { month: month2, day, year, iso };
57289
57568
  }
57290
57569
  function parseMonthDay(raw) {
57291
57570
  const match = raw.trim().match(/^(\w+)\s+(\d{1,2})$/);
57292
57571
  if (!match)
57293
57572
  return null;
57294
- const month = MONTH_MAP[match[1].toLowerCase()];
57295
- if (!month)
57573
+ const month2 = MONTH_MAP[match[1].toLowerCase()];
57574
+ if (!month2)
57296
57575
  return null;
57297
- return { month, day: parseInt(match[2], 10) };
57576
+ return { month: month2, day: parseInt(match[2], 10) };
57298
57577
  }
57299
57578
  function parseAmexPdfDate(raw, meta3) {
57300
57579
  const parsed = parseMonthDay(raw);
@@ -57697,10 +57976,10 @@ function parseMonthNameDate(raw) {
57697
57976
  const match = raw.trim().match(/^(\d{1,2})\s+(\w{3})\w*,?\s+(\d{4})$/);
57698
57977
  if (!match)
57699
57978
  return null;
57700
- const month = MONTH_NAMES[match[2].toLowerCase().slice(0, 3)];
57701
- if (!month)
57979
+ const month2 = MONTH_NAMES[match[2].toLowerCase().slice(0, 3)];
57980
+ if (!month2)
57702
57981
  return null;
57703
- return `${match[3]}-${month}-${match[1].padStart(2, "0")}`;
57982
+ return `${match[3]}-${month2}-${match[1].padStart(2, "0")}`;
57704
57983
  }
57705
57984
  function parseDdMmYyyy(raw) {
57706
57985
  const match = raw.trim().match(/^(\d{2})\/(\d{2})\/(\d{4})/);
@@ -57998,13 +58277,13 @@ function parseHdfcDate(raw) {
57998
58277
  if (!match)
57999
58278
  return null;
58000
58279
  const day = match[1];
58001
- const month = match[2];
58280
+ const month2 = match[2];
58002
58281
  let year = match[3];
58003
58282
  if (year.length === 2) {
58004
58283
  const num = parseInt(year, 10);
58005
58284
  year = num > 50 ? `19${year}` : `20${year}`;
58006
58285
  }
58007
- return `${year}-${month}-${day}`;
58286
+ return `${year}-${month2}-${day}`;
58008
58287
  }
58009
58288
  function parseAmount3(raw) {
58010
58289
  if (!raw)
@@ -59177,603 +59456,231 @@ function exportTools(client, crypto3) {
59177
59456
  ];
59178
59457
  }
59179
59458
 
59180
- // ../../node_modules/.bun/@typesafe-ai+sdk@0.6.0/node_modules/@typesafe-ai/sdk/dist/index.mjs
59181
- var requestIdFrom = (headers) => headers.get("x-typesafe-request-id") ?? undefined;
59182
- var APIPromise = class APIPromise2 extends Promise {
59183
- #responsePromise;
59184
- #parseResponse;
59185
- #parsed;
59186
- constructor(responsePromise, parseResponse) {
59187
- super((resolve) => resolve(undefined));
59188
- this.#responsePromise = responsePromise;
59189
- this.#parseResponse = parseResponse;
59190
- }
59191
- asResponse() {
59192
- return this.#responsePromise;
59193
- }
59194
- async withResponse() {
59195
- const [data, response] = await Promise.all([this.#parse(), this.#responsePromise]);
59196
- return {
59197
- data,
59198
- response,
59199
- requestId: requestIdFrom(response.headers)
59200
- };
59201
- }
59202
- map(fn2) {
59203
- return new APIPromise2(this.#responsePromise, () => this.#parse().then(fn2));
59204
- }
59205
- #parse() {
59206
- this.#parsed ??= this.#responsePromise.then(this.#parseResponse);
59207
- return this.#parsed;
59208
- }
59209
- then(onfulfilled, onrejected) {
59210
- return this.#parse().then(onfulfilled, onrejected);
59211
- }
59212
- catch(onrejected) {
59213
- return this.#parse().catch(onrejected);
59214
- }
59215
- finally(onfinally) {
59216
- return this.#parse().finally(onfinally);
59217
- }
59218
- };
59219
- var ENV = {
59220
- apiKey: "TYPESAFE_API_KEY",
59221
- baseURL: "TYPESAFE_BASE_URL",
59222
- defaultModel: "TYPESAFE_DEFAULT_MODEL",
59223
- logLevel: "TYPESAFE_LOG_LEVEL"
59224
- };
59225
- var readEnv = (name) => {
59226
- if (typeof process === "undefined" || !process.env)
59227
- return;
59228
- return process.env[name]?.trim() || undefined;
59229
- };
59230
- var fromCodeOrEnv = (fromCode, envVar) => fromCode ?? readEnv(envVar);
59231
- var range = (from, to2) => Array.from({ length: to2 - from }, (_2, i2) => from + i2);
59232
- var DEFAULT_RETRY_POLICY = {
59233
- maxRetries: 2,
59234
- backoffInitialMs: 500,
59235
- backoffMaxMs: 5000,
59236
- backoffJitter: 0.25,
59237
- httpStatuses: /* @__PURE__ */ new Set([
59238
- 408,
59239
- 429,
59240
- ...range(500, 600)
59241
- ]),
59242
- respectRetryAfter: true,
59243
- maxRetryAfterMs: 60000,
59244
- apiConnectionError: true,
59245
- apiTimeoutError: true
59246
- };
59247
- DEFAULT_RETRY_POLICY.maxRetries;
59248
- var isRetryableStatus = (status, policy = DEFAULT_RETRY_POLICY) => policy.httpStatuses.has(status);
59249
- var parseRetryAfter = (headers, now = Date.now()) => {
59250
- const ms = Number(headers.get("retry-after-ms"));
59251
- if (headers.has("retry-after-ms") && Number.isFinite(ms) && ms >= 0)
59252
- return ms;
59253
- const raw = headers.get("retry-after");
59254
- if (raw === null)
59255
- return;
59256
- const seconds = Number(raw);
59257
- if (Number.isFinite(seconds))
59258
- return seconds >= 0 ? seconds * 1000 : undefined;
59259
- const date5 = Date.parse(raw);
59260
- if (!Number.isNaN(date5))
59261
- return Math.max(0, date5 - now);
59262
- };
59263
- var retryDelayMs = (attempt, headers, policy = DEFAULT_RETRY_POLICY, random = Math.random) => {
59264
- if (policy.respectRetryAfter && headers !== undefined) {
59265
- const retryAfter = parseRetryAfter(headers);
59266
- if (retryAfter !== undefined && retryAfter <= policy.maxRetryAfterMs)
59267
- return retryAfter;
59268
- }
59269
- const exponential = Math.min(policy.backoffInitialMs * 2 ** attempt, policy.backoffMaxMs);
59270
- return Math.round(exponential * (1 - random() * policy.backoffJitter));
59271
- };
59272
- var sleep3 = (ms, signal) => new Promise((resolve, reject) => {
59273
- if (signal?.aborted)
59274
- return reject(signal.reason);
59275
- const onAbort = () => {
59276
- clearTimeout(timer);
59277
- reject(signal?.reason);
59278
- };
59279
- const timer = setTimeout(() => {
59280
- signal?.removeEventListener("abort", onAbort);
59281
- resolve();
59282
- }, ms);
59283
- signal?.addEventListener("abort", onAbort, { once: true });
59284
- });
59285
- var TypeSafeError = class extends Error {
59286
- constructor(message, options) {
59287
- super(message, options);
59288
- this.name = new.target.name;
59289
- }
59290
- };
59291
- var isRecord = (value) => typeof value === "object" && value !== null;
59292
- var extractMessage = (body) => {
59293
- if (typeof body === "string")
59294
- return body || undefined;
59295
- if (!isRecord(body))
59296
- return;
59297
- const { error: error62, message, detail } = body;
59298
- if (typeof error62 === "string")
59299
- return error62;
59300
- if (isRecord(error62) && typeof error62.message === "string")
59301
- return error62.message;
59302
- if (typeof message === "string")
59303
- return message;
59304
- if (typeof detail === "string")
59305
- return detail;
59306
- if (isRecord(detail) && typeof detail.message === "string")
59307
- return detail.message;
59308
- if (Array.isArray(detail))
59309
- return describeValidationErrors(detail);
59310
- };
59311
- var describeValidationErrors = (errors3) => {
59312
- const parts = errors3.flatMap((e2) => {
59313
- if (!isRecord(e2) || typeof e2.msg !== "string")
59314
- return [];
59315
- const loc = Array.isArray(e2.loc) ? e2.loc.filter((x2) => x2 !== "body").join(".") : "";
59316
- return [loc ? `${loc}: ${e2.msg}` : e2.msg];
59317
- });
59318
- return parts.length > 0 ? parts.join("; ") : undefined;
59319
- };
59320
- var MAX_RAW_BODY_IN_MESSAGE = 200;
59321
- var APIError = class APIError2 extends TypeSafeError {
59322
- status;
59323
- headers;
59324
- body;
59325
- requestId;
59326
- constructor(status, body, headers, message) {
59327
- super(message ?? APIError2.describe(status, body));
59328
- this.status = status;
59329
- this.body = body;
59330
- this.headers = headers;
59331
- this.requestId = requestIdFrom(headers);
59332
- }
59333
- static describe(status, body) {
59334
- const detail = extractMessage(body);
59335
- if (detail)
59336
- return `${status} ${detail}`;
59337
- if (body === undefined)
59338
- return `${status} status code (no body)`;
59339
- const raw = typeof body === "string" ? body : JSON.stringify(body);
59340
- return `${status} ${raw.length > MAX_RAW_BODY_IN_MESSAGE ? `${raw.slice(0, MAX_RAW_BODY_IN_MESSAGE)}…` : raw}`;
59341
- }
59342
- static fromResponse(status, body, headers) {
59343
- if (status === 400)
59344
- return new BadRequestError(status, body, headers);
59345
- if (status === 401)
59346
- return new AuthenticationError(status, body, headers);
59347
- if (status === 403)
59348
- return new PermissionDeniedError(status, body, headers);
59349
- if (status === 404)
59350
- return new NotFoundError(status, body, headers);
59351
- if (status === 422)
59352
- return new UnprocessableEntityError(status, body, headers);
59353
- if (status === 429)
59354
- return new RateLimitError(status, body, headers);
59355
- if (status >= 500)
59356
- return new InternalServerError(status, body, headers);
59357
- return new APIError2(status, body, headers);
59358
- }
59359
- };
59360
- var BadRequestError = class extends APIError {
59361
- };
59362
- var AuthenticationError = class extends APIError {
59363
- };
59364
- var PermissionDeniedError = class extends APIError {
59365
- };
59366
- var NotFoundError = class extends APIError {
59367
- };
59368
- var UnprocessableEntityError = class extends APIError {
59369
- };
59370
- var RateLimitError = class extends APIError {
59371
- retryAfterMs = parseRetryAfter(this.headers);
59372
- };
59373
- var InternalServerError = class extends APIError {
59374
- };
59375
- var APIConnectionError = class extends TypeSafeError {
59376
- constructor(message = "Connection error.", options) {
59377
- super(message, options);
59378
- }
59379
- };
59380
- var APITimeoutError = class extends APIConnectionError {
59381
- timeoutMs;
59382
- constructor(timeoutMs, options) {
59383
- super(`Request timed out after ${timeoutMs}ms.`, options);
59384
- this.timeoutMs = timeoutMs;
59385
- }
59386
- };
59387
- var APIUserAbortError = class extends TypeSafeError {
59388
- constructor(message = "Request was aborted.", options) {
59389
- super(message, options);
59390
- }
59391
- };
59392
- var LOG_LEVELS = [
59393
- "debug",
59394
- "info",
59395
- "warn",
59396
- "error",
59397
- "off"
59398
- ];
59399
- var DEFAULT_LOG_LEVEL = "warn";
59400
- var isLogLevel = (value) => LOG_LEVELS.includes(value);
59401
- var parseLogLevel = (value, source) => {
59402
- if (isLogLevel(value))
59403
- return value;
59404
- throw new TypeSafeError(`Invalid log level "${value}" from ${source}. Expected one of: ${LOG_LEVELS.join(", ")}.`);
59405
- };
59406
- var PREFIX = "[typesafe-sdk]";
59407
- var consoleLogger = {
59408
- debug: (message, ...args) => console.debug(`${PREFIX} ${message}`, ...args),
59409
- info: (message, ...args) => console.info(`${PREFIX} ${message}`, ...args),
59410
- warn: (message, ...args) => console.warn(`${PREFIX} ${message}`, ...args),
59411
- error: (message, ...args) => console.error(`${PREFIX} ${message}`, ...args)
59412
- };
59413
- var RANK = {
59414
- debug: 0,
59415
- info: 1,
59416
- warn: 2,
59417
- error: 3,
59418
- off: 4
59419
- };
59420
- var drop = () => {};
59421
- var withLevel = (sink, level) => {
59422
- const enabled = (at2) => RANK[at2] >= RANK[level];
59423
- return {
59424
- debug: enabled("debug") ? (message, ...args) => sink.debug(message, ...args) : drop,
59425
- info: enabled("info") ? (message, ...args) => sink.info(message, ...args) : drop,
59426
- warn: enabled("warn") ? (message, ...args) => sink.warn(message, ...args) : drop,
59427
- error: enabled("error") ? (message, ...args) => sink.error(message, ...args) : drop
59428
- };
59429
- };
59430
- var KEY_HEADERS = /* @__PURE__ */ new Set([
59431
- "authorization",
59432
- "proxy-authorization",
59433
- "x-api-key"
59434
- ]);
59435
- var OPAQUE_HEADERS = /* @__PURE__ */ new Set(["cookie", "set-cookie"]);
59436
- var redactKey = (value) => {
59437
- const [scheme, secret] = value.includes(" ") ? value.split(/\s+/, 2) : [undefined, value];
59438
- const tail = secret && secret.length > 8 ? secret.slice(-4) : "";
59439
- return `${scheme ? `${scheme} ` : ""}***${tail}`;
59440
- };
59441
- var redact = (name, value) => {
59442
- const lower = name.toLowerCase();
59443
- if (KEY_HEADERS.has(lower))
59444
- return redactKey(value);
59445
- if (OPAQUE_HEADERS.has(lower))
59446
- return "***";
59447
- return value;
59448
- };
59449
- var redactHeaders = (headers) => Object.fromEntries(Object.entries(headers).map(([name, value]) => [name, redact(name, value)]));
59450
- var choice = (instructions, criteria) => {
59451
- if (Array.isArray(criteria))
59452
- throw new TypeSafeError("Choice criteria must be a map of labels to descriptions, not a list.");
59453
- return {
59454
- type: "choice",
59455
- instructions,
59456
- criteria
59457
- };
59458
- };
59459
- var validateQuestions = (questions) => {
59460
- if (Object.keys(questions).length === 0)
59461
- throw new TypeSafeError("At least one question is required.");
59462
- for (const [name, question] of Object.entries(questions)) {
59463
- if (question.type !== "score")
59464
- continue;
59465
- if (!Array.isArray(question.criteria))
59466
- 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.`);
59467
- if (question.criteria.length < 2)
59468
- throw new TypeSafeError(`Score question "${name}" has ${question.criteria.length} criteria; at least two scores are required.`);
59469
- }
59470
- };
59471
- var Models = class {
59472
- #transport;
59473
- constructor(transport) {
59474
- this.#transport = transport;
59475
- }
59476
- list(options = {}) {
59477
- return this.#transport.request("GET", "/v1/models", options).map(unwrapModels);
59478
- }
59479
- };
59480
- var unwrapModels = (wire) => {
59481
- if (Array.isArray(wire?.models))
59482
- return wire.models;
59483
- throw new TypeSafeError("Unexpected response shape from GET /v1/models; expected { models: [...] }.");
59484
- };
59485
- var g2 = globalThis;
59486
- var isBrowser = () => typeof g2.window !== "undefined" && typeof g2.window.document !== "undefined" && typeof g2.navigator !== "undefined";
59487
- var describeRuntime = () => {
59488
- const platform = g2.process?.platform && g2.process?.arch ? ` (${g2.process.platform}; ${g2.process.arch})` : "";
59489
- if (g2.Bun?.version)
59490
- return `bun/${g2.Bun.version}${platform}`;
59491
- if (g2.Deno?.version?.deno)
59492
- return `deno/${g2.Deno.version.deno}${platform}`;
59493
- if (g2.EdgeRuntime !== undefined)
59494
- return "vercel-edge";
59495
- if (g2.navigator?.userAgent === "Cloudflare-Workers")
59496
- return "cloudflare-workers";
59497
- if (g2.process?.versions?.node)
59498
- return `node/${g2.process.versions.node}${platform}`;
59499
- if (isBrowser())
59500
- return "browser";
59501
- return "unknown";
59502
- };
59503
- var VERSION2 = "0.6.0";
59504
- var missingApiKey = () => {
59505
- throw new TypeSafeError(`No API key was provided. Pass \`apiKey\` to the TypeSafeClient constructor or set the ${ENV.apiKey} environment variable.`);
59506
- };
59507
- var missingFetch = () => {
59508
- throw new TypeSafeError("No global `fetch` is available in this runtime. Pass a `fetch` implementation to the TypeSafeClient constructor.");
59509
- };
59510
- var refuseBrowser = () => {
59511
- 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.");
59512
- };
59513
- var defaultFetch = (input2, init) => globalThis.fetch(input2, init);
59514
- var assertNonNegativeInteger = (name, value) => {
59515
- if (!Number.isInteger(value) || value < 0)
59516
- throw new TypeSafeError(`\`${name}\` must be a non-negative integer, got ${String(value)}.`);
59517
- return value;
59518
- };
59519
- var assertPositiveMs = (name, value) => {
59520
- if (!Number.isFinite(value) || value <= 0)
59521
- throw new TypeSafeError(`\`${name}\` must be a positive number of milliseconds, got ${String(value)}.`);
59522
- return value;
59523
- };
59524
- var assertNonNegativeMs = (name, value) => {
59525
- if (!Number.isFinite(value) || value < 0)
59526
- throw new TypeSafeError(`\`${name}\` must be a non-negative number of milliseconds, got ${String(value)}.`);
59527
- return value;
59528
- };
59529
- var assertFraction = (name, value) => {
59530
- if (!Number.isFinite(value) || value < 0 || value > 1)
59531
- throw new TypeSafeError(`\`${name}\` must be between 0 and 1, got ${String(value)}.`);
59532
- return value;
59533
- };
59534
- var assertStatusSet = (name, statuses) => {
59535
- for (const status of statuses)
59536
- if (!Number.isInteger(status) || status < 100 || status > 999)
59537
- throw new TypeSafeError(`\`${name}\` must contain HTTP status codes, got ${String(status)}.`);
59538
- return statuses;
59539
- };
59540
- var resolveRetryPolicy = (base, overrides) => {
59541
- const o2 = overrides ?? {};
59542
- return {
59543
- maxRetries: o2.maxRetries === undefined ? base.maxRetries : assertNonNegativeInteger("retry.maxRetries", o2.maxRetries),
59544
- backoffInitialMs: o2.backoffInitialMs === undefined ? base.backoffInitialMs : assertNonNegativeMs("retry.backoffInitialMs", o2.backoffInitialMs),
59545
- backoffMaxMs: o2.backoffMaxMs === undefined ? base.backoffMaxMs : assertNonNegativeMs("retry.backoffMaxMs", o2.backoffMaxMs),
59546
- backoffJitter: o2.backoffJitter === undefined ? base.backoffJitter : assertFraction("retry.backoffJitter", o2.backoffJitter),
59547
- httpStatuses: new Set(o2.httpStatuses === undefined ? base.httpStatuses : assertStatusSet("retry.httpStatuses", o2.httpStatuses)),
59548
- respectRetryAfter: o2.respectRetryAfter ?? base.respectRetryAfter,
59549
- maxRetryAfterMs: o2.maxRetryAfterMs === undefined ? base.maxRetryAfterMs : assertNonNegativeMs("retry.maxRetryAfterMs", o2.maxRetryAfterMs),
59550
- apiConnectionError: o2.apiConnectionError ?? base.apiConnectionError,
59551
- apiTimeoutError: o2.apiTimeoutError ?? base.apiTimeoutError
59552
- };
59553
- };
59554
- var isRetryableError = (err, policy) => {
59555
- if (err instanceof APITimeoutError)
59556
- return policy.apiTimeoutError;
59557
- if (err instanceof APIConnectionError)
59558
- return policy.apiConnectionError;
59559
- return false;
59560
- };
59561
- var resolveLogLevel = (fromCode) => {
59562
- if (fromCode !== undefined)
59563
- return parseLogLevel(fromCode, "the `logLevel` option");
59564
- const fromEnv = readEnv(ENV.logLevel);
59565
- if (fromEnv !== undefined)
59566
- return parseLogLevel(fromEnv, ENV.logLevel);
59567
- return DEFAULT_LOG_LEVEL;
59568
- };
59569
- var stripTrailingSlashes = (url3) => url3.replace(/\/+$/, "");
59570
- var mergeHeaders = (...sources) => {
59571
- const entries = /* @__PURE__ */ new Map;
59572
- for (const source of sources)
59573
- for (const [name, value] of Object.entries(source))
59574
- if (value === undefined)
59575
- entries.delete(name.toLowerCase());
59576
- else
59577
- entries.set(name.toLowerCase(), [name, value]);
59578
- return Object.fromEntries(entries.values());
59579
- };
59580
- var bufferResponse = async (response, signal) => {
59581
- const reader = response.clone().body?.getReader();
59582
- if (!reader)
59583
- return;
59584
- const cancel = () => {
59585
- reader.cancel(signal.reason).catch(() => {});
59586
- response.body?.cancel(signal.reason).catch(() => {});
59587
- };
59588
- signal.addEventListener("abort", cancel, { once: true });
59589
- try {
59590
- if (signal.aborted)
59591
- cancel();
59592
- signal.throwIfAborted();
59593
- while (!(await reader.read()).done)
59594
- signal.throwIfAborted();
59595
- signal.throwIfAborted();
59596
- } finally {
59597
- signal.removeEventListener("abort", cancel);
59598
- reader.releaseLock();
59599
- }
59600
- };
59601
- var RUNTIME = describeRuntime();
59602
- var TypeSafeClient = class {
59603
- #apiKey;
59604
- baseURL;
59605
- defaultModel;
59606
- logLevel;
59607
- logger;
59608
- retry;
59609
- timeout;
59610
- defaultHeaders;
59611
- fetch;
59612
- models;
59613
- #requestCount = 0;
59614
- constructor(config2 = {}) {
59615
- if (isBrowser() && !config2.dangerouslyAllowBrowser)
59616
- refuseBrowser();
59617
- this.#apiKey = fromCodeOrEnv(config2.apiKey, ENV.apiKey) ?? missingApiKey();
59618
- this.baseURL = stripTrailingSlashes(fromCodeOrEnv(config2.baseURL, ENV.baseURL) ?? "https://api.typesafe.ai");
59619
- this.defaultModel = fromCodeOrEnv(config2.defaultModel, ENV.defaultModel) ?? "jev-latest";
59620
- this.logLevel = resolveLogLevel(config2.logLevel);
59621
- this.logger = withLevel(config2.logger ?? consoleLogger, this.logLevel);
59622
- this.retry = resolveRetryPolicy(DEFAULT_RETRY_POLICY, config2.retry);
59623
- this.timeout = assertPositiveMs("timeout", config2.timeout ?? 1e4);
59624
- this.defaultHeaders = { ...config2.defaultHeaders };
59625
- if (config2.fetch === undefined && typeof globalThis.fetch !== "function")
59626
- missingFetch();
59627
- this.fetch = config2.fetch ?? defaultFetch;
59628
- const transport = {
59629
- request: (method, path, options) => this.#request(method, path, options),
59630
- defaultModel: this.defaultModel
59631
- };
59632
- this.models = new Models(transport);
59633
- }
59634
- systemOne(request, options = {}) {
59635
- validateQuestions(request.questions);
59636
- const body = {
59637
- ...request,
59638
- model: request.model ?? this.defaultModel
59639
- };
59640
- return this.#request("POST", "/v1/systemone", {
59641
- ...options,
59642
- body
59643
- });
59459
+ // ../../node_modules/.bun/@nimit9+signet-ai@0.1.11+a8a356da5edd5fb9/node_modules/@nimit9/signet-ai/dist/jev.js
59460
+ var JEV_DEFAULT_MODEL = "jev-1.13.0";
59461
+ var JEV_DEFAULT_MIN_CONFIDENCE = 0.95;
59462
+ var JEV_DEFAULT_TIMEOUT_MS = 8000;
59463
+ var JEV_DEFAULT_BASE_URL = "https://api.typesafe.ai";
59464
+ function fallbackOf(q2, reason, extra = {}) {
59465
+ const d2 = { value: q2.fallback, confident: false, source: "fallback", reason };
59466
+ if (extra.confidence !== undefined)
59467
+ d2.confidence = extra.confidence;
59468
+ if (extra.answer !== undefined)
59469
+ d2.answer = extra.answer;
59470
+ if (extra.probability !== undefined)
59471
+ d2.probability = extra.probability;
59472
+ return d2;
59473
+ }
59474
+ function labelsOf(options) {
59475
+ return Array.isArray(options) ? [...options] : Object.keys(options);
59476
+ }
59477
+ function isUnit(n2) {
59478
+ return typeof n2 === "number" && Number.isFinite(n2) && n2 >= 0 && n2 <= 1;
59479
+ }
59480
+ function validQuestion(q2) {
59481
+ if (!q2 || typeof q2.question !== "string")
59482
+ return false;
59483
+ if (q2.type === "choice") {
59484
+ const labels = labelsOf(q2.options);
59485
+ return labels.length >= 1 && labels.length <= 255 && labels.includes(q2.fallback);
59486
+ }
59487
+ if (q2.type === "score") {
59488
+ return Array.isArray(q2.levels) && q2.levels.length >= 2 && q2.levels.length <= 10;
59489
+ }
59490
+ return q2.type === "noul";
59491
+ }
59492
+ function toWire(q2) {
59493
+ if (q2.type === "choice") {
59494
+ const criteria = Array.isArray(q2.options) ? Object.fromEntries(q2.options.map((l2) => [l2, null])) : q2.options;
59495
+ return { type: "choice", instructions: q2.question, criteria };
59496
+ }
59497
+ if (q2.type === "score")
59498
+ return { type: "score", instructions: q2.question, criteria: q2.levels };
59499
+ return { type: "noul", instructions: q2.question };
59500
+ }
59501
+ function judge(q2, raw, min) {
59502
+ const a2 = raw;
59503
+ if (!a2 || typeof a2 !== "object")
59504
+ return fallbackOf(q2, "invalid_response");
59505
+ if (q2.type === "noul") {
59506
+ const p2 = a2.noul;
59507
+ if (!isUnit(p2))
59508
+ return fallbackOf(q2, "invalid_response");
59509
+ const confidence2 = Math.max(p2, 1 - p2);
59510
+ const answer2 = p2 >= 0.5;
59511
+ if (confidence2 < min) {
59512
+ return fallbackOf(q2, "low_confidence", { confidence: confidence2, answer: answer2, probability: p2 });
59513
+ }
59514
+ return { value: answer2, confident: true, source: "jev", confidence: confidence2, probability: p2 };
59515
+ }
59516
+ const confidence = a2.confidence;
59517
+ if (!isUnit(confidence))
59518
+ return fallbackOf(q2, "invalid_response");
59519
+ let answer;
59520
+ if (q2.type === "choice") {
59521
+ if (typeof a2.choice !== "string" || !labelsOf(q2.options).includes(a2.choice)) {
59522
+ return fallbackOf(q2, "invalid_response", { confidence });
59523
+ }
59524
+ answer = a2.choice;
59525
+ } else {
59526
+ const s2 = a2.score;
59527
+ if (typeof s2 !== "number" || !Number.isFinite(s2) || s2 < 0 || s2 > q2.levels.length - 1) {
59528
+ return fallbackOf(q2, "invalid_response", { confidence });
59529
+ }
59530
+ answer = s2;
59644
59531
  }
59645
- #request(method, path, options = {}) {
59646
- const resolved = {
59647
- method,
59648
- path,
59649
- body: options.body,
59650
- headers: mergeHeaders(this.defaultHeaders, options.headers ?? {}),
59651
- signal: options.signal,
59652
- timeout: options.timeout === undefined ? this.timeout : assertPositiveMs("timeout", options.timeout),
59653
- retry: resolveRetryPolicy(this.retry, options.retry)
59654
- };
59655
- const tag = `#${++this.#requestCount} ${method} ${path}`;
59656
- return new APIPromise(this.fetchWithRetries(tag, resolved), async (res) => {
59657
- const parsed = await parseBody(res);
59658
- this.logger.debug(`${tag} <- body`, parsed);
59659
- return parsed;
59660
- });
59532
+ if (confidence < min)
59533
+ return fallbackOf(q2, "low_confidence", { confidence, answer });
59534
+ return { value: answer, confident: true, source: "jev", confidence };
59535
+ }
59536
+
59537
+ class Timeout {
59538
+ }
59539
+ function createJev(config2) {
59540
+ const apiKey = typeof config2.apiKey === "string" ? config2.apiKey.trim() : "";
59541
+ const model = config2.model ?? JEV_DEFAULT_MODEL;
59542
+ const floor = config2.minConfidence ?? JEV_DEFAULT_MIN_CONFIDENCE;
59543
+ const timeoutMs = config2.timeoutMs ?? JEV_DEFAULT_TIMEOUT_MS;
59544
+ const allowSensitive = config2.allowSensitive === true;
59545
+ const url3 = `${(config2.baseUrl ?? JEV_DEFAULT_BASE_URL).replace(/\/+$/, "")}/v1/systemone`;
59546
+ function emit(event) {
59547
+ if (!config2.onEvent)
59548
+ return;
59549
+ try {
59550
+ config2.onEvent(event);
59551
+ } catch {}
59661
59552
  }
59662
- async fetchWithRetries(tag, req) {
59663
- const url3 = `${this.baseURL}${req.path}`;
59664
- const headers = mergeHeaders(req.headers, {
59665
- Authorization: `Bearer ${this.#apiKey}`,
59666
- Accept: "application/json",
59667
- "User-Agent": `typesafe-sdk/${VERSION2}`,
59668
- "X-TypeSafe-SDK": `typesafe-sdk/${VERSION2}`,
59669
- "X-TypeSafe-Runtime": RUNTIME,
59670
- "Content-Type": req.body === undefined ? undefined : "application/json",
59671
- "X-TypeSafe-Retry-Count": undefined
59672
- });
59673
- const body = req.body === undefined ? undefined : JSON.stringify(req.body);
59674
- for (let attempt = 0;; attempt++) {
59675
- const retriesLeft = req.retry.maxRetries - attempt;
59676
- const attemptHeaders = attempt === 0 ? headers : {
59677
- ...headers,
59678
- "X-TypeSafe-Retry-Count": String(attempt)
59553
+ function finish(questions, decisions, meta3) {
59554
+ const durationMs = Date.now() - meta3.started;
59555
+ for (const [name, d2] of Object.entries(decisions)) {
59556
+ const event = {
59557
+ kind: questions[name].type,
59558
+ name,
59559
+ source: d2.source,
59560
+ sensitive: meta3.sensitive,
59561
+ model,
59562
+ durationMs
59679
59563
  };
59680
- this.logger.debug(`${tag} -> ${url3}`, {
59681
- headers: redactHeaders(attemptHeaders),
59682
- body: req.body
59683
- });
59684
- const started = Date.now();
59685
- let res;
59686
- try {
59687
- res = await this.attempt(tag, url3, {
59688
- method: req.method,
59689
- headers: attemptHeaders,
59690
- body
59691
- }, req);
59692
- } catch (err) {
59693
- if (err instanceof APIUserAbortError || retriesLeft <= 0)
59694
- throw err;
59695
- if (!isRetryableError(err, req.retry))
59696
- throw err;
59697
- await this.backOff(tag, attempt, retriesLeft, err.message, undefined, req);
59698
- continue;
59699
- }
59700
- const requestId = requestIdFrom(res.headers);
59701
- this.logger.info(`${tag} <- ${res.status} in ${Date.now() - started}ms${requestId ? ` (request ${requestId})` : ""}`);
59702
- if (res.ok)
59703
- return res;
59704
- const errorBody = await parseBody(res);
59705
- this.logger.debug(`${tag} <- error body`, errorBody);
59706
- const error62 = APIError.fromResponse(res.status, errorBody, res.headers);
59707
- if (retriesLeft <= 0 || !isRetryableStatus(res.status, req.retry))
59708
- throw error62;
59709
- await this.backOff(tag, attempt, retriesLeft, `${res.status}`, res.headers, req);
59564
+ if (d2.reason)
59565
+ event.reason = d2.reason;
59566
+ if (d2.confidence !== undefined)
59567
+ event.confidence = d2.confidence;
59568
+ if (meta3.status !== undefined && d2.reason === "http_error")
59569
+ event.status = meta3.status;
59570
+ emit(event);
59710
59571
  }
59572
+ return decisions;
59711
59573
  }
59712
- async attempt(tag, url3, init, { signal, timeout }) {
59713
- const controller = new AbortController;
59714
- const abortFromCaller = () => controller.abort(signal?.reason);
59715
- if (signal?.aborted)
59716
- abortFromCaller();
59717
- signal?.addEventListener("abort", abortFromCaller, { once: true });
59718
- let timedOut = false;
59719
- const timer = setTimeout(() => {
59720
- timedOut = true;
59721
- controller.abort();
59722
- }, timeout);
59574
+ async function run(questions, base) {
59723
59575
  const started = Date.now();
59724
- const elapsed = () => `${Date.now() - started}ms`;
59576
+ const sensitive = base.sensitive === true;
59577
+ const all2 = (reason, status2) => finish(questions, Object.fromEntries(Object.entries(questions).map(([n2, q2]) => [n2, fallbackOf(q2, reason)])), { sensitive, started, status: status2 });
59578
+ if (base.sensitive !== false && !allowSensitive)
59579
+ return all2("sensitive_not_allowed");
59580
+ if (!apiKey)
59581
+ return all2("no_key");
59582
+ const min = base.minConfidence ?? floor;
59583
+ const names = Object.keys(questions);
59584
+ if (!isUnit(min) || names.length === 0 || !names.every((n2) => validQuestion(questions[n2]))) {
59585
+ return all2("invalid_request");
59586
+ }
59587
+ if (base.signal?.aborted)
59588
+ return all2("aborted");
59589
+ const controller = new AbortController;
59590
+ const onAbort = () => controller.abort();
59591
+ base.signal?.addEventListener("abort", onAbort, { once: true });
59592
+ let timer;
59593
+ const timeout = new Promise((resolve) => {
59594
+ timer = setTimeout(() => {
59595
+ controller.abort();
59596
+ resolve(new Timeout);
59597
+ }, timeoutMs);
59598
+ });
59599
+ const doFetch = config2.fetch ?? ((u2, i2) => globalThis.fetch(u2, i2));
59600
+ let status;
59725
59601
  try {
59726
- const response = await this.fetch(url3, {
59727
- ...init,
59728
- signal: controller.signal
59729
- });
59730
- await bufferResponse(response, controller.signal);
59731
- return response;
59602
+ const request = (async () => {
59603
+ const res = await doFetch(url3, {
59604
+ method: "POST",
59605
+ headers: {
59606
+ Authorization: `Bearer ${apiKey}`,
59607
+ Accept: "application/json",
59608
+ "Content-Type": "application/json"
59609
+ },
59610
+ body: JSON.stringify({
59611
+ model,
59612
+ state: base.state,
59613
+ questions: Object.fromEntries(names.map((n2) => [n2, toWire(questions[n2])]))
59614
+ }),
59615
+ signal: controller.signal
59616
+ });
59617
+ status = res.status;
59618
+ if (!res.ok)
59619
+ return { ok: false };
59620
+ return { ok: true, body: await res.json() };
59621
+ })();
59622
+ const out = await Promise.race([request, timeout]);
59623
+ if (out instanceof Timeout) {
59624
+ request.catch(() => {});
59625
+ return all2("timeout");
59626
+ }
59627
+ if (!out.ok)
59628
+ return all2("http_error", status);
59629
+ const answers = out.body?.answers;
59630
+ if (!answers || typeof answers !== "object")
59631
+ return all2("invalid_response");
59632
+ return finish(questions, Object.fromEntries(names.map((n2) => [n2, judge(questions[n2], answers[n2], min)])), { sensitive, started });
59732
59633
  } catch (err) {
59733
- if (signal?.aborted) {
59734
- this.logger.info(`${tag} aborted by caller after ${elapsed()}`);
59735
- throw new APIUserAbortError(undefined, { cause: err });
59736
- }
59737
- if (timedOut) {
59738
- this.logger.info(`${tag} timed out after ${elapsed()}`);
59739
- throw new APITimeoutError(timeout, { cause: err });
59740
- }
59741
- this.logger.info(`${tag} connection error after ${elapsed()}`, err);
59742
- throw new APIConnectionError(err instanceof Error ? `Connection error: ${err.message}` : undefined, { cause: err });
59634
+ if (base.signal?.aborted)
59635
+ return all2("aborted");
59636
+ if (status !== undefined)
59637
+ return all2("invalid_response");
59638
+ return all2(err?.name === "TimeoutError" ? "timeout" : "network_error");
59743
59639
  } finally {
59744
59640
  clearTimeout(timer);
59745
- signal?.removeEventListener("abort", abortFromCaller);
59641
+ base.signal?.removeEventListener("abort", onAbort);
59746
59642
  }
59747
59643
  }
59748
- async backOff(tag, attempt, retriesLeft, reason, headers, { retry, signal }) {
59749
- const delay = retryDelayMs(attempt, headers, retry);
59750
- const nth = attempt + 1;
59751
- const total = attempt + retriesLeft;
59752
- this.logger.info(`${tag} retrying in ${delay}ms (retry ${nth}/${total}) after ${reason}`);
59753
- try {
59754
- await sleep3(delay, signal);
59755
- } catch (err) {
59756
- this.logger.info(`${tag} aborted by caller while waiting to retry`);
59757
- throw new APIUserAbortError(undefined, { cause: err });
59758
- }
59644
+ function baseOf(args) {
59645
+ return {
59646
+ state: args.state,
59647
+ sensitive: args.sensitive,
59648
+ minConfidence: args.minConfidence,
59649
+ signal: args.signal
59650
+ };
59759
59651
  }
59760
- };
59761
- var parseBody = async (res) => {
59762
- const text = await res.text();
59763
- if (text.length === 0)
59764
- return;
59765
- if ((res.headers.get("content-type") ?? "").includes("application/json"))
59766
- try {
59767
- return JSON.parse(text);
59768
- } catch {
59769
- return text;
59652
+ return {
59653
+ enabled: apiKey.length > 0,
59654
+ async choice(args) {
59655
+ const q2 = {
59656
+ type: "choice",
59657
+ question: args.question,
59658
+ options: args.options,
59659
+ fallback: args.fallback
59660
+ };
59661
+ const { result } = await run({ result: q2 }, baseOf(args));
59662
+ return result;
59663
+ },
59664
+ async score(args) {
59665
+ const q2 = {
59666
+ type: "score",
59667
+ question: args.question,
59668
+ levels: args.levels,
59669
+ fallback: args.fallback
59670
+ };
59671
+ const { result } = await run({ result: q2 }, baseOf(args));
59672
+ return result;
59673
+ },
59674
+ async noul(args) {
59675
+ const q2 = { type: "noul", question: args.question, fallback: args.fallback };
59676
+ const { result } = await run({ result: q2 }, baseOf(args));
59677
+ return result;
59678
+ },
59679
+ async batch(args) {
59680
+ return await run(args.questions, baseOf(args));
59770
59681
  }
59771
- try {
59772
- return JSON.parse(text);
59773
- } catch {
59774
- return text;
59775
- }
59776
- };
59682
+ };
59683
+ }
59777
59684
 
59778
59685
  // src/lib/jev-categorize.ts
59779
59686
  var JEV_MODEL = "jev-1.13.0";
@@ -59793,18 +59700,18 @@ function assertJevAllowed(crypto3, env2 = process.env) {
59793
59700
  throw new Error("JEV_API_KEY is not set — Jev categorization is off.");
59794
59701
  return key;
59795
59702
  }
59796
- function makeJevClassifier(apiKey) {
59797
- const client = new TypeSafeClient({ apiKey, timeout: TIMEOUT_MS });
59798
- return async (state, options) => {
59799
- const { answers } = await client.systemOne({
59800
- model: JEV_MODEL,
59801
- state,
59802
- questions: {
59803
- category: choice("Which spending category best fits this bank transaction?", options)
59804
- }
59805
- });
59806
- return { choice: answers.category.choice, confidence: answers.category.confidence };
59807
- };
59703
+ function jevAllowSensitive(crypto3, env2) {
59704
+ return crypto3 === undefined || env2.JEV_ALLOW_PRIVATE_MODE === "1";
59705
+ }
59706
+ function makeJev(apiKey, crypto3, env2 = process.env, fetchImpl) {
59707
+ return createJev({
59708
+ apiKey,
59709
+ model: JEV_MODEL,
59710
+ minConfidence: DEFAULT_MIN_CONFIDENCE,
59711
+ timeoutMs: TIMEOUT_MS,
59712
+ allowSensitive: jevAllowSensitive(crypto3, env2),
59713
+ ...fetchImpl ? { fetch: fetchImpl } : {}
59714
+ });
59808
59715
  }
59809
59716
  var UNSURE = "unsure";
59810
59717
  function optionsFor(type, index) {
@@ -59822,26 +59729,33 @@ function groupState(type, sample) {
59822
59729
  const dir = type === "credit" ? "Money received" : "Money paid out";
59823
59730
  return `${dir}. Bank narration: "${sample.slice(0, 80)}"`;
59824
59731
  }
59825
- async function jevCategorize(client, crypto3, classify, opts = {}) {
59732
+ async function jevCategorize(client, crypto3, jev, opts = {}) {
59826
59733
  assertPrivateModeAllowed(crypto3, opts.env);
59827
- const min = opts.minConfidence ?? DEFAULT_MIN_CONFIDENCE;
59828
59734
  const index = await loadCategoryIndex(client);
59829
59735
  const rows = await fetchUncategorized(client, crypto3, opts);
59830
59736
  const groups = groupRows(rows);
59831
- const unparsed = groups.filter((g3) => g3.pattern === UNPARSED).length;
59832
- const candidates = groups.filter((g3) => g3.pattern !== UNPARSED);
59737
+ const unparsed = groups.filter((g2) => g2.pattern === UNPARSED).length;
59738
+ const candidates = groups.filter((g2) => g2.pattern !== UNPARSED);
59833
59739
  let errors3 = 0;
59834
- const suggestions = await mapLimit(candidates, CONCURRENCY, async (g3) => {
59835
- try {
59836
- const options = optionsFor(g3.type, index);
59837
- const sample = g3.rows[0].description;
59838
- const text = crypto3 ? g3.pattern : sample;
59839
- const r2 = await classify(groupState(g3.type, text), options);
59840
- return r2.choice in options ? { g: g3, ...r2 } : null;
59841
- } catch {
59842
- errors3 += 1;
59843
- return null;
59740
+ const suggestions = await mapLimit(candidates, CONCURRENCY, async (g2) => {
59741
+ const options = optionsFor(g2.type, index);
59742
+ const sample = g2.rows[0].description;
59743
+ const text = crypto3 ? g2.pattern : sample;
59744
+ const d2 = await jev.choice({
59745
+ question: "Which spending category best fits this bank transaction?",
59746
+ options,
59747
+ fallback: UNSURE,
59748
+ state: groupState(g2.type, text),
59749
+ sensitive: true,
59750
+ minConfidence: opts.minConfidence
59751
+ });
59752
+ if (d2.confident)
59753
+ return { g: g2, choice: d2.value, confidence: d2.confidence, confident: true };
59754
+ if (d2.reason === "low_confidence" && d2.answer !== undefined) {
59755
+ return { g: g2, choice: d2.answer, confidence: d2.confidence ?? 0, confident: false };
59844
59756
  }
59757
+ errors3 += 1;
59758
+ return null;
59845
59759
  });
59846
59760
  const accepted = [];
59847
59761
  const review = [];
@@ -59849,7 +59763,7 @@ async function jevCategorize(client, crypto3, classify, opts = {}) {
59849
59763
  if (!s2)
59850
59764
  continue;
59851
59765
  const isTransfer = index.bySlug.get(s2.choice)?.type === "transfer";
59852
- if (s2.choice !== UNSURE && !isTransfer && s2.confidence >= min) {
59766
+ if (s2.confident && s2.choice !== UNSURE && !isTransfer) {
59853
59767
  accepted.push({
59854
59768
  pattern: s2.g.pattern,
59855
59769
  type: s2.g.type,
@@ -59898,7 +59812,8 @@ function jevTools(client, crypto3) {
59898
59812
  }),
59899
59813
  handler: async ({ startDate, endDate, minConfidence, dryRun }) => {
59900
59814
  const key = assertJevAllowed(crypto3);
59901
- const result = await jevCategorize(client, crypto3, makeJevClassifier(key), {
59815
+ const jev = makeJev(key, crypto3);
59816
+ const result = await jevCategorize(client, crypto3, jev, {
59902
59817
  startDate,
59903
59818
  endDate,
59904
59819
  minConfidence,
@@ -59936,15 +59851,7 @@ function settingsTools(client) {
59936
59851
  describe: { name: "", ownerLabels: "Owner slug → display name" },
59937
59852
  variants: { profile: profile.update, household: household.update }
59938
59853
  });
59939
- return [
59940
- get.tool,
59941
- update.tool,
59942
- get.alias("get_profile", "profile"),
59943
- get.alias("get_household", "household"),
59944
- get.alias("get_encryption_status", "encryption"),
59945
- update.alias("update_profile", "profile"),
59946
- update.alias("update_household", "household")
59947
- ];
59854
+ return [get.tool, update.tool];
59948
59855
  }
59949
59856
 
59950
59857
  // src/tools/statement-import.ts
@@ -60230,15 +60137,7 @@ function statementImportTools(client, crypto3) {
60230
60137
  })
60231
60138
  }
60232
60139
  });
60233
- return [
60234
- preview.tool,
60235
- commit.tool,
60236
- 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" }]),
60237
- preview.alias("import_csv_parse", "csv"),
60238
- commit.alias("import_csv_confirm", "csv"),
60239
- commit.alias("import_statement_batch", "rows"),
60240
- commit.alias("import_statement_file", "json_file")
60241
- ];
60140
+ return [preview.tool, commit.tool];
60242
60141
  }
60243
60142
 
60244
60143
  // src/registrations.ts
@@ -60263,10 +60162,127 @@ var REGISTRATIONS = [
60263
60162
  { title: "Settings", tools: settingsTools },
60264
60163
  { title: "Encryption & privacy", tools: authCryptoTools }
60265
60164
  ];
60165
+
60166
+ // src/resources.ts
60167
+ var MERCHANT_CAP = 500;
60168
+ var JSON_MIME = "application/json";
60169
+ var RESOURCES = [
60170
+ {
60171
+ name: "categories",
60172
+ uri: "paisa://categories",
60173
+ title: "Categories",
60174
+ description: "Category tree (id, slug, name, type, children). Same as list_entities categories.",
60175
+ tool: "list_entities",
60176
+ args: { entity: "categories" }
60177
+ },
60178
+ {
60179
+ name: "bank_accounts",
60180
+ uri: "paisa://bank-accounts",
60181
+ title: "Bank accounts",
60182
+ description: "Bank accounts and cards (id, name, bank, type, last4, owner).",
60183
+ tool: "list_entities",
60184
+ args: { entity: "bank_accounts" }
60185
+ },
60186
+ {
60187
+ name: "persons",
60188
+ uri: "paisa://persons",
60189
+ title: "Persons",
60190
+ description: "People you send/receive money from (decrypted in private mode).",
60191
+ tool: "list_entities",
60192
+ args: { entity: "persons" }
60193
+ },
60194
+ {
60195
+ name: "merchants",
60196
+ uri: "paisa://merchants",
60197
+ title: "Merchants",
60198
+ description: `Merchants with their category (first ${MERCHANT_CAP}; look others up with list_entities entity='merchants' rawId).`,
60199
+ tool: "list_entities",
60200
+ args: { entity: "merchants" },
60201
+ shape: capMerchants
60202
+ },
60203
+ {
60204
+ name: "household",
60205
+ uri: "paisa://household",
60206
+ title: "Household",
60207
+ description: "Household name, currency, locale, owner labels and members.",
60208
+ tool: "get_settings",
60209
+ args: { section: "household" }
60210
+ },
60211
+ {
60212
+ name: "profile",
60213
+ uri: "paisa://profile",
60214
+ title: "Profile",
60215
+ description: "Your name, email and owner slug.",
60216
+ tool: "get_settings",
60217
+ args: { section: "profile" }
60218
+ }
60219
+ ];
60220
+ var TRANSACTION_TEMPLATE = "paisa://transactions/{id}";
60221
+ function capMerchants(value) {
60222
+ const v2 = value;
60223
+ if (!v2 || !Array.isArray(v2.data) || v2.data.length <= MERCHANT_CAP)
60224
+ return value;
60225
+ return {
60226
+ ...v2,
60227
+ data: v2.data.slice(0, MERCHANT_CAP),
60228
+ truncated: {
60229
+ returned: MERCHANT_CAP,
60230
+ total: v2.data.length,
60231
+ hint: "Use list_entities entity='merchants' rawId=<UPI ID or name> to find one."
60232
+ }
60233
+ };
60234
+ }
60235
+ function scrubCiphertext(value) {
60236
+ const scrub = (row) => {
60237
+ if (row && typeof row === "object" && typeof row.cipher === "string") {
60238
+ Object.assign(row, { cipher: null, undecrypted: true });
60239
+ }
60240
+ };
60241
+ const data = value?.data;
60242
+ if (Array.isArray(data))
60243
+ data.forEach(scrub);
60244
+ else
60245
+ scrub(data);
60246
+ return value;
60247
+ }
60248
+ async function runTool(tool, args, mcp) {
60249
+ const input2 = tool.input ? await tool.input.parseAsync(args) : args;
60250
+ const ctx = { tool: tool.name, signal: mcp.mcpReq.signal, mcp };
60251
+ return tool.handler(input2, ctx);
60252
+ }
60253
+ function jsonContents(uri, value) {
60254
+ return { contents: [{ uri: uri.href, mimeType: JSON_MIME, text: JSON.stringify(value ?? null) }] };
60255
+ }
60256
+ function registerResources(server, tools) {
60257
+ const byName = new Map(tools.map((t2) => [t2.name, t2]));
60258
+ const need = (name) => {
60259
+ const t2 = byName.get(name);
60260
+ if (!t2)
60261
+ throw new Error(`MCP resources: tool ${name} is not registered`);
60262
+ return t2;
60263
+ };
60264
+ for (const r2 of RESOURCES) {
60265
+ const tool = need(r2.tool);
60266
+ server.registerResource(r2.name, r2.uri, { title: r2.title, description: r2.description, mimeType: JSON_MIME }, async (uri, mcp) => {
60267
+ const value = scrubCiphertext(await runTool(tool, r2.args, mcp));
60268
+ return jsonContents(uri, r2.shape ? r2.shape(value) : value);
60269
+ });
60270
+ }
60271
+ const getTransaction = need("get_transaction");
60272
+ server.registerResource("transaction", new ResourceTemplate(TRANSACTION_TEMPLATE, { list: undefined }), {
60273
+ title: "Transaction",
60274
+ description: "One transaction by UUID (same as get_transaction; decrypted in private mode).",
60275
+ mimeType: JSON_MIME
60276
+ }, async (uri, variables, mcp) => {
60277
+ const id = Array.isArray(variables.id) ? variables.id[0] : variables.id;
60278
+ const value = scrubCiphertext(await runTool(getTransaction, { id }, mcp));
60279
+ return jsonContents(uri, value);
60280
+ });
60281
+ }
60266
60282
  // package.json
60267
60283
  var package_default = {
60268
60284
  name: "paisa-mcp",
60269
- version: "0.1.0",
60285
+ version: "0.3.0",
60270
60286
  repository: {
60271
60287
  type: "git",
60272
60288
  url: "git+https://github.com/nimit9/paisa.git",
@@ -60293,13 +60309,14 @@ var package_default = {
60293
60309
  devDependencies: {
60294
60310
  "@modelcontextprotocol/client": "^2.1.0",
60295
60311
  "@modelcontextprotocol/server": "^2.1.0",
60312
+ "@nimit9/signet-ai": "^0.1.11",
60313
+ "@nimit9/signet-lib": "^0.1.14",
60296
60314
  "@nimit9/signet-server": "^0.2.2",
60297
60315
  "@paisa/parsers": "workspace:*",
60298
60316
  "@paisa/reconciliation": "workspace:*",
60299
60317
  "@paisa/types": "workspace:*",
60300
60318
  "@scure/bip39": "^2.2.0",
60301
60319
  "@types/bun": "^1.3.14",
60302
- "@typesafe-ai/sdk": "^0.6.0",
60303
60320
  axios: "^1.16.1",
60304
60321
  "hash-wasm": "^4.12.0",
60305
60322
  typescript: "^7.0.2",
@@ -60311,7 +60328,7 @@ var package_default = {
60311
60328
  };
60312
60329
 
60313
60330
  // src/version.ts
60314
- var VERSION3 = package_default.version;
60331
+ var VERSION2 = package_default.version;
60315
60332
 
60316
60333
  // src/server.ts
60317
60334
  function paisaErrorResult(err) {
@@ -60323,29 +60340,18 @@ function paisaErrorResult(err) {
60323
60340
  function buildServer(client, crypto3) {
60324
60341
  const tools = applyAnnotations(REGISTRATIONS.flatMap((group) => group.tools(client, crypto3)));
60325
60342
  for (const t2 of tools)
60326
- if (t2.input && !isAlias(t2))
60343
+ if (t2.input)
60327
60344
  withCompactJsonSchema(t2.input);
60328
60345
  const server = createMcpServer({
60329
60346
  name: "paisa",
60330
- version: VERSION3,
60347
+ version: VERSION2,
60331
60348
  tools,
60332
60349
  errorResult: paisaErrorResult
60333
60350
  });
60334
- hideFromToolsList(server, new Set(tools.filter(isAlias).map((t2) => t2.name)));
60351
+ registerResources(server, tools);
60352
+ registerPrompts(server);
60335
60353
  return server;
60336
60354
  }
60337
- function hideFromToolsList(server, hidden) {
60338
- if (hidden.size === 0)
60339
- return;
60340
- const inner = server.server;
60341
- const list = inner._getRequestHandler("tools/list");
60342
- if (!list)
60343
- throw new Error("MCP SDK: no tools/list handler to wrap (aliases would be listed)");
60344
- server.server.setRequestHandler("tools/list", async (request, ctx) => {
60345
- const result = await list(request, ctx);
60346
- return { ...result, tools: result.tools.filter((t2) => !hidden.has(t2.name)) };
60347
- });
60348
- }
60349
60355
 
60350
60356
  // src/index.ts
60351
60357
  async function main() {
@@ -60368,5 +60374,5 @@ main().catch((err) => {
60368
60374
  process.exit(1);
60369
60375
  });
60370
60376
 
60371
- //# debugId=FDF0CD88C245260A64756E2164756E21
60377
+ //# debugId=C64445D8B55EDCFA64756E2164756E21
60372
60378
  //# sourceMappingURL=index.js.map