iranti 0.3.41 → 0.4.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.
Files changed (99) hide show
  1. package/dist/scripts/iranti-cli.js +7 -4
  2. package/dist/scripts/iranti-mcp.js +9 -5
  3. package/dist/scripts/setup.js +2 -1
  4. package/dist/src/api/middleware/auth.d.ts +2 -0
  5. package/dist/src/api/middleware/auth.d.ts.map +1 -1
  6. package/dist/src/api/middleware/auth.js +2 -0
  7. package/dist/src/api/middleware/auth.js.map +1 -1
  8. package/dist/src/api/routes/batch.js +2 -1
  9. package/dist/src/api/routes/batch.js.map +1 -1
  10. package/dist/src/api/routes/consumerMcp.d.ts +25 -0
  11. package/dist/src/api/routes/consumerMcp.d.ts.map +1 -0
  12. package/dist/src/api/routes/consumerMcp.js +197 -0
  13. package/dist/src/api/routes/consumerMcp.js.map +1 -0
  14. package/dist/src/api/routes/feedback.d.ts +13 -0
  15. package/dist/src/api/routes/feedback.d.ts.map +1 -0
  16. package/dist/src/api/routes/feedback.js +111 -0
  17. package/dist/src/api/routes/feedback.js.map +1 -0
  18. package/dist/src/api/routes/knowledge.d.ts.map +1 -1
  19. package/dist/src/api/routes/knowledge.js +9 -5
  20. package/dist/src/api/routes/knowledge.js.map +1 -1
  21. package/dist/src/api/routes/memory.d.ts.map +1 -1
  22. package/dist/src/api/routes/memory.js +2 -1
  23. package/dist/src/api/routes/memory.js.map +1 -1
  24. package/dist/src/api/routes/tokens.d.ts +18 -0
  25. package/dist/src/api/routes/tokens.d.ts.map +1 -0
  26. package/dist/src/api/routes/tokens.js +89 -0
  27. package/dist/src/api/routes/tokens.js.map +1 -0
  28. package/dist/src/api/server.js +24 -1
  29. package/dist/src/api/server.js.map +1 -1
  30. package/dist/src/attendant/AttendantInstance.d.ts +19 -0
  31. package/dist/src/attendant/AttendantInstance.d.ts.map +1 -1
  32. package/dist/src/attendant/AttendantInstance.js +257 -6
  33. package/dist/src/attendant/AttendantInstance.js.map +1 -1
  34. package/dist/src/generated/prisma/browser.d.ts +15 -0
  35. package/dist/src/generated/prisma/browser.d.ts.map +1 -1
  36. package/dist/src/generated/prisma/client.d.ts +17 -2
  37. package/dist/src/generated/prisma/client.d.ts.map +1 -1
  38. package/dist/src/generated/prisma/client.js +2 -2
  39. package/dist/src/generated/prisma/commonInputTypes.d.ts +363 -243
  40. package/dist/src/generated/prisma/commonInputTypes.d.ts.map +1 -1
  41. package/dist/src/generated/prisma/enums.d.ts +24 -0
  42. package/dist/src/generated/prisma/enums.d.ts.map +1 -1
  43. package/dist/src/generated/prisma/enums.js +22 -1
  44. package/dist/src/generated/prisma/enums.js.map +1 -1
  45. package/dist/src/generated/prisma/internal/class.d.ts +40 -7
  46. package/dist/src/generated/prisma/internal/class.d.ts.map +1 -1
  47. package/dist/src/generated/prisma/internal/class.js +4 -4
  48. package/dist/src/generated/prisma/internal/class.js.map +1 -1
  49. package/dist/src/generated/prisma/internal/prismaNamespace.d.ts +304 -14
  50. package/dist/src/generated/prisma/internal/prismaNamespace.d.ts.map +1 -1
  51. package/dist/src/generated/prisma/internal/prismaNamespace.js +45 -7
  52. package/dist/src/generated/prisma/internal/prismaNamespace.js.map +1 -1
  53. package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.d.ts +46 -5
  54. package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.d.ts.map +1 -1
  55. package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.js +45 -7
  56. package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.js.map +1 -1
  57. package/dist/src/generated/prisma/models/Archive.d.ts +0 -10
  58. package/dist/src/generated/prisma/models/Archive.d.ts.map +1 -1
  59. package/dist/src/generated/prisma/models/Feedback.d.ts +1328 -0
  60. package/dist/src/generated/prisma/models/Feedback.d.ts.map +1 -0
  61. package/dist/src/generated/prisma/models/Feedback.js +3 -0
  62. package/dist/src/generated/prisma/models/Feedback.js.map +1 -0
  63. package/dist/src/generated/prisma/models/KnowledgeEntry.d.ts +349 -17
  64. package/dist/src/generated/prisma/models/KnowledgeEntry.d.ts.map +1 -1
  65. package/dist/src/generated/prisma/models/Token.d.ts +1329 -0
  66. package/dist/src/generated/prisma/models/Token.d.ts.map +1 -0
  67. package/dist/src/generated/prisma/models/Token.js +3 -0
  68. package/dist/src/generated/prisma/models/Token.js.map +1 -0
  69. package/dist/src/generated/prisma/models/User.d.ts +1545 -0
  70. package/dist/src/generated/prisma/models/User.d.ts.map +1 -0
  71. package/dist/src/generated/prisma/models/User.js +3 -0
  72. package/dist/src/generated/prisma/models/User.js.map +1 -0
  73. package/dist/src/generated/prisma/models.d.ts +3 -0
  74. package/dist/src/generated/prisma/models.d.ts.map +1 -1
  75. package/dist/src/librarian/source-reliability.d.ts.map +1 -1
  76. package/dist/src/librarian/source-reliability.js +4 -2
  77. package/dist/src/librarian/source-reliability.js.map +1 -1
  78. package/dist/src/library/agent-registry.d.ts.map +1 -1
  79. package/dist/src/library/agent-registry.js +12 -6
  80. package/dist/src/library/agent-registry.js.map +1 -1
  81. package/dist/src/library/queries.d.ts.map +1 -1
  82. package/dist/src/library/queries.js +4 -2
  83. package/dist/src/library/queries.js.map +1 -1
  84. package/dist/src/sdk/index.d.ts +1 -0
  85. package/dist/src/sdk/index.d.ts.map +1 -1
  86. package/dist/src/sdk/index.js +1 -1
  87. package/dist/src/sdk/index.js.map +1 -1
  88. package/dist/src/security/apiKeys.d.ts +2 -0
  89. package/dist/src/security/apiKeys.d.ts.map +1 -1
  90. package/dist/src/security/apiKeys.js +8 -4
  91. package/dist/src/security/apiKeys.js.map +1 -1
  92. package/dist/src/security/consumerTokens.d.ts +52 -0
  93. package/dist/src/security/consumerTokens.d.ts.map +1 -0
  94. package/dist/src/security/consumerTokens.js +98 -0
  95. package/dist/src/security/consumerTokens.js.map +1 -0
  96. package/package.json +1 -1
  97. package/prisma/migrations/20260414191958_add_multi_tenant_identity/migration.sql +106 -0
  98. package/prisma/migrations/20260415030000_add_feedback_table/migration.sql +35 -0
  99. package/prisma/schema.prisma +84 -2
@@ -2329,20 +2329,23 @@ try {
2329
2329
  const pendingEdits = debt.pendingEdits || 0;
2330
2330
  const lastEditAt = debt.lastEditAt || null;
2331
2331
 
2332
- if (pendingEdits < 1) {
2332
+ // Block only after this many unique file edits are pending.
2333
+ const WRITE_DEBT_BLOCK_THRESHOLD = 5;
2334
+
2335
+ if (pendingEdits < WRITE_DEBT_BLOCK_THRESHOLD) {
2333
2336
  process.exit(0);
2334
2337
  }
2335
2338
 
2336
2339
  const output = JSON.stringify({
2337
2340
  hookEventName: 'PreToolUse',
2338
2341
  permissionDecision: 'deny',
2339
- permissionDecisionReason: \`Iranti write-guard: \${pendingEdits} file edit(s) since last iranti_write (last edit: \${lastEditAt || 'unknown'}). Call iranti_write for each pending edit before making new changes.\`,
2342
+ permissionDecisionReason: \`Iranti write-guard: \${pendingEdits} file edit(s) pending iranti_write (threshold: \${WRITE_DEBT_BLOCK_THRESHOLD}). Call iranti_write for pending edits before making new changes.\`,
2340
2343
  additionalContext: [
2341
2344
  'IRANTI WRITE-GUARD BLOCKED THIS EDIT.',
2342
2345
  \`You have \${pendingEdits} pending file edit(s) that have not been written to Iranti shared memory.\`,
2343
- 'Before making any more edits, you MUST call iranti_write for each pending edit.',
2346
+ \`Before making any more edits, call iranti_write for pending edits to bring debt below \${WRITE_DEBT_BLOCK_THRESHOLD}.\`,
2344
2347
  'Include: entity (project/[id]/file/[filename]), absolutePath, lines changed, what changed and why.',
2345
- 'After writing all pending edits, this guard will allow new edits.',
2348
+ 'After writing pending edits below the threshold, this guard will allow new edits.',
2346
2349
  ].join('\\n'),
2347
2350
  });
2348
2351
 
@@ -89,6 +89,10 @@ const WATCHER_EXCLUDE_DIRS = new Set([
89
89
  '__pycache__', '.cache', '.iranti',
90
90
  ]);
91
91
  const WRITE_DEBT_FILENAME = '.iranti-write-debt';
92
+ // Block attend only after this many unique file edits are pending — avoids
93
+ // false-positive blocks on single-file changes while still enforcing logging
94
+ // discipline when a meaningful batch of edits has accumulated.
95
+ const WRITE_DEBT_BLOCK_THRESHOLD = 5;
92
96
  let _activeWatcher = null;
93
97
  let _watchedCwd = null;
94
98
  let _watchDebounceTimer = null;
@@ -453,7 +457,7 @@ function startFileWatcher(cwd) {
453
457
  }
454
458
  }
455
459
  /**
456
- * Read .iranti-write-debt and return an isError gate result when debt > 0.
460
+ * Read .iranti-write-debt and return an isError gate result when debt >= WRITE_DEBT_BLOCK_THRESHOLD.
457
461
  * Returns null when attend should proceed normally.
458
462
  */
459
463
  function checkWriteDebtForAttend(cwd) {
@@ -463,7 +467,7 @@ function checkWriteDebtForAttend(cwd) {
463
467
  return null;
464
468
  const debt = JSON.parse(node_fs_1.default.readFileSync(debtFile, 'utf8'));
465
469
  const pendingEdits = debt.pendingEdits || 0;
466
- if (pendingEdits < 1)
470
+ if (pendingEdits < WRITE_DEBT_BLOCK_THRESHOLD)
467
471
  return null;
468
472
  const edits = debt.edits || [];
469
473
  const fileList = edits.length > 0
@@ -473,12 +477,12 @@ function checkWriteDebtForAttend(cwd) {
473
477
  content: [{
474
478
  type: 'text',
475
479
  text: [
476
- `WRITE_GUARD_BLOCKED: ${pendingEdits} file edit(s) pending iranti_write.`,
480
+ `WRITE_GUARD_BLOCKED: ${pendingEdits} file edit(s) pending iranti_write (threshold: ${WRITE_DEBT_BLOCK_THRESHOLD}).`,
477
481
  `Changed files (most recent ${Math.min(pendingEdits, 10)}):`,
478
482
  fileList,
479
483
  '',
480
484
  `Required: call iranti_write for each changed file before iranti_attend can proceed.`,
481
- `After writing all pending edits, re-call iranti_attend to get your memory brief.`,
485
+ `After writing pending edits below the threshold, re-call iranti_attend to get your memory brief.`,
482
486
  ].join('\n'),
483
487
  }],
484
488
  isError: true,
@@ -801,7 +805,7 @@ async function main() {
801
805
  // DB startup is deferred to first tool call via dbStartup() — see above.
802
806
  const server = new mcp_js_1.McpServer({
803
807
  name: 'iranti-mcp',
804
- version: '0.3.41',
808
+ version: '0.4.1',
805
809
  });
806
810
  server.registerTool('iranti_handshake', {
807
811
  description: `Initialize or refresh an agent's working-memory brief for the current task.
@@ -64,7 +64,8 @@ async function isSeeded() {
64
64
  try {
65
65
  const entry = await (0, client_1.getDb)().knowledgeEntry.findUnique({
66
66
  where: {
67
- entityType_entityId_key: {
67
+ userId_entityType_entityId_key: {
68
+ userId: 1,
68
69
  entityType: 'system',
69
70
  entityId: 'library',
70
71
  key: 'initialization_log',
@@ -19,6 +19,8 @@ export interface IrantiAuthContext {
19
19
  keyId: string;
20
20
  owner: string;
21
21
  scopes: string[];
22
+ userId: number;
23
+ tokenScope: string;
22
24
  }
23
25
  declare global {
24
26
  namespace Express {
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../src/api/middleware/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,OAAO,CAAC;QACd,UAAU,OAAO;YACb,UAAU,CAAC,EAAE,iBAAiB,CAAC;SAClC;KACJ;CACJ;AAmBD,wBAAsB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBjG"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../src/api/middleware/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,OAAO,CAAC,MAAM,CAAC;IACX,UAAU,OAAO,CAAC;QACd,UAAU,OAAO;YACb,UAAU,CAAC,EAAE,iBAAiB,CAAC;SAClC;KACJ;CACJ;AAmBD,wBAAsB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBjG"}
@@ -45,6 +45,8 @@ async function authenticate(req, res, next) {
45
45
  keyId: result.keyId ?? 'unknown',
46
46
  owner: result.owner ?? 'unknown',
47
47
  scopes: result.scopes ?? ['*'],
48
+ userId: result.userId ?? 1,
49
+ tokenScope: result.tokenScope ?? 'dev_cli',
48
50
  };
49
51
  next();
50
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../src/api/middleware/auth.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAqCH,oCAiBC;AAnDD,oDAAwD;AAiBxD,SAAS,aAAa,CAAC,GAAY;IAC/B,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACzE,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/D,OAAO,SAAS,CAAC,IAAI,EAAE,CAAC;IAC5B,CAAC;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACpE,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAE5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9B,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAChD,CAAC;AAEM,KAAK,UAAU,YAAY,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IAC9E,MAAM,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAEvC,MAAM,MAAM,GAAG,MAAM,IAAA,wBAAc,EAAC,WAAW,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACb,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,oDAAoD,EAAE,CAAC,CAAC;QACvH,OAAO;IACX,CAAC;IAED,GAAG,CAAC,UAAU,GAAG;QACb,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,UAAU;QAC/B,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,SAAS;QAChC,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,SAAS;QAChC,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC;KACjC,CAAC;IAEF,IAAI,EAAE,CAAC;AACX,CAAC"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../src/api/middleware/auth.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;AAuCH,oCAmBC;AAvDD,oDAAwD;AAmBxD,SAAS,aAAa,CAAC,GAAY;IAC/B,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACzE,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/D,OAAO,SAAS,CAAC,IAAI,EAAE,CAAC;IAC5B,CAAC;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACpE,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAE5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9B,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAChD,CAAC;AAEM,KAAK,UAAU,YAAY,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IAC9E,MAAM,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAEvC,MAAM,MAAM,GAAG,MAAM,IAAA,wBAAc,EAAC,WAAW,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACb,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,oDAAoD,EAAE,CAAC,CAAC;QACvH,OAAO;IACX,CAAC;IAED,GAAG,CAAC,UAAU,GAAG;QACb,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,UAAU;QAC/B,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,SAAS;QAChC,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,SAAS;QAChC,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC;QAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC;QAC1B,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,SAAS;KAC7C,CAAC;IAEF,IAAI,EAAE,CAAC;AACX,CAAC"}
@@ -39,7 +39,8 @@ exports.batchRouter.post("/", async (req, res) => {
39
39
  try {
40
40
  const row = await db.knowledgeEntry.findUnique({
41
41
  where: {
42
- entityType_entityId_key: {
42
+ userId_entityType_entityId_key: {
43
+ userId: 1,
43
44
  entityType,
44
45
  entityId,
45
46
  key: it.key,
@@ -1 +1 @@
1
- {"version":3,"file":"batch.js","sourceRoot":"","sources":["../../../../src/api/routes/batch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"batch.js","sourceRoot":"","sources":["../../../../src/api/routes/batch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Consumer MCP route — `/mcp/:token`
3
+ *
4
+ * Serves a simplified, user-scoped Model Context Protocol tool surface for
5
+ * Claude.ai, ChatGPT, and other MCP-compatible AI frontends.
6
+ *
7
+ * Authentication: the raw token is extracted from the URL path, hashed with
8
+ * SHA256+pepper, and looked up in the `tokens` table. A missing, revoked, or
9
+ * wrong-scope token returns 401 before any MCP handshake begins.
10
+ *
11
+ * Tool surface (scope mcp_consumer_t0):
12
+ * - iranti_remember — store a personal memory
13
+ * - iranti_recall — retrieve a personal memory
14
+ * - iranti_forget — delete a personal memory
15
+ *
16
+ * Transport: MCP Streamable HTTP (stateless — one transport per request).
17
+ * Supports both POST (tool calls) and GET (SSE event stream) on the same path,
18
+ * as required by the MCP spec.
19
+ *
20
+ * All reads and writes are scoped to the `userId` resolved from the token, so
21
+ * no cross-user data leakage is possible at the query layer.
22
+ */
23
+ import { Router } from 'express';
24
+ export declare function consumerMcpRoutes(): Router;
25
+ //# sourceMappingURL=consumerMcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consumerMcp.d.ts","sourceRoot":"","sources":["../../../../src/api/routes/consumerMcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAQpD,wBAAgB,iBAAiB,IAAI,MAAM,CA0K1C"}
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ /**
3
+ * Consumer MCP route — `/mcp/:token`
4
+ *
5
+ * Serves a simplified, user-scoped Model Context Protocol tool surface for
6
+ * Claude.ai, ChatGPT, and other MCP-compatible AI frontends.
7
+ *
8
+ * Authentication: the raw token is extracted from the URL path, hashed with
9
+ * SHA256+pepper, and looked up in the `tokens` table. A missing, revoked, or
10
+ * wrong-scope token returns 401 before any MCP handshake begins.
11
+ *
12
+ * Tool surface (scope mcp_consumer_t0):
13
+ * - iranti_remember — store a personal memory
14
+ * - iranti_recall — retrieve a personal memory
15
+ * - iranti_forget — delete a personal memory
16
+ *
17
+ * Transport: MCP Streamable HTTP (stateless — one transport per request).
18
+ * Supports both POST (tool calls) and GET (SSE event stream) on the same path,
19
+ * as required by the MCP spec.
20
+ *
21
+ * All reads and writes are scoped to the `userId` resolved from the token, so
22
+ * no cross-user data leakage is possible at the query layer.
23
+ */
24
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ var desc = Object.getOwnPropertyDescriptor(m, k);
27
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
28
+ desc = { enumerable: true, get: function() { return m[k]; } };
29
+ }
30
+ Object.defineProperty(o, k2, desc);
31
+ }) : (function(o, m, k, k2) {
32
+ if (k2 === undefined) k2 = k;
33
+ o[k2] = m[k];
34
+ }));
35
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
36
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
37
+ }) : function(o, v) {
38
+ o["default"] = v;
39
+ });
40
+ var __importStar = (this && this.__importStar) || (function () {
41
+ var ownKeys = function(o) {
42
+ ownKeys = Object.getOwnPropertyNames || function (o) {
43
+ var ar = [];
44
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
45
+ return ar;
46
+ };
47
+ return ownKeys(o);
48
+ };
49
+ return function (mod) {
50
+ if (mod && mod.__esModule) return mod;
51
+ var result = {};
52
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
53
+ __setModuleDefault(result, mod);
54
+ return result;
55
+ };
56
+ })();
57
+ Object.defineProperty(exports, "__esModule", { value: true });
58
+ exports.consumerMcpRoutes = consumerMcpRoutes;
59
+ const express_1 = require("express");
60
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
61
+ const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
62
+ const z = __importStar(require("zod/v4"));
63
+ const client_1 = require("../../library/client");
64
+ const apiKeys_1 = require("../../security/apiKeys");
65
+ function consumerMcpRoutes() {
66
+ const router = (0, express_1.Router)();
67
+ // Handle both GET (SSE) and POST (tool calls) on the same path
68
+ router.all('/:token', async (req, res) => {
69
+ const rawToken = Array.isArray(req.params['token']) ? req.params['token'][0] : req.params['token'];
70
+ if (!rawToken) {
71
+ res.status(401).json({ error: 'Missing token' });
72
+ return;
73
+ }
74
+ const db = (0, client_1.getDb)();
75
+ // ── Token authentication ──────────────────────────────────────────────────
76
+ const tokenHash = (0, apiKeys_1.hashApiKeySecret)(rawToken);
77
+ const tokenRecord = await db.token.findUnique({ where: { tokenHash } });
78
+ if (!tokenRecord || tokenRecord.revokedAt !== null) {
79
+ res.status(401).json({ error: 'Invalid or revoked token' });
80
+ return;
81
+ }
82
+ // Consumer endpoint only serves mcp_consumer_* scopes
83
+ if (!tokenRecord.scope.startsWith('mcp_consumer')) {
84
+ res.status(403).json({ error: 'Token scope does not permit MCP consumer access' });
85
+ return;
86
+ }
87
+ const userId = tokenRecord.userId;
88
+ // Fire-and-forget lastUsedAt update (non-blocking)
89
+ db.token.update({ where: { tokenHash }, data: { lastUsedAt: new Date() } }).catch(() => { });
90
+ // ── MCP server setup (stateless — one per request) ────────────────────────
91
+ const mcpServer = new mcp_js_1.McpServer({
92
+ name: 'iranti',
93
+ version: '1.0.0',
94
+ });
95
+ // iranti_remember — store a personal memory
96
+ mcpServer.tool('iranti_remember', 'Store a personal memory in your Iranti knowledge base.', {
97
+ entity: z.string().describe('Entity identifier. Use "person/me" for personal facts.'),
98
+ key: z.string().describe('Fact key — a stable identifier for this memory (e.g. "dietary_preference")'),
99
+ value: z.string().describe('The fact value to remember'),
100
+ summary: z.string().optional().describe('Short human-readable summary (auto-derived from value if omitted)'),
101
+ }, async ({ entity, key, value, summary }) => {
102
+ const [entityType, ...rest] = entity.split('/');
103
+ const entityId = rest.join('/') || 'me';
104
+ const valueSummary = summary ?? value.slice(0, 120);
105
+ const valueRaw = { text: value };
106
+ await db.knowledgeEntry.upsert({
107
+ where: {
108
+ userId_entityType_entityId_key: { userId, entityType, entityId, key },
109
+ },
110
+ update: {
111
+ valueRaw,
112
+ valueSummary,
113
+ source: `mcp_consumer`,
114
+ updatedAt: new Date(),
115
+ lastAccessedAt: new Date(),
116
+ },
117
+ create: {
118
+ userId,
119
+ entityType,
120
+ entityId,
121
+ key,
122
+ valueRaw,
123
+ valueSummary,
124
+ confidence: 80,
125
+ source: `mcp_consumer`,
126
+ createdBy: `user/${userId}`,
127
+ surface: tokenRecord.surface,
128
+ conflictLog: [],
129
+ },
130
+ });
131
+ return {
132
+ content: [{ type: 'text', text: `Remembered: ${entity}/${key} = "${valueSummary}"` }],
133
+ };
134
+ });
135
+ // iranti_recall — retrieve a personal memory
136
+ mcpServer.tool('iranti_recall', 'Retrieve a personal memory from your Iranti knowledge base.', {
137
+ entity: z.string().describe('Entity identifier (e.g. "person/me")'),
138
+ key: z.string().describe('Fact key to retrieve'),
139
+ }, async ({ entity, key }) => {
140
+ const [entityType, ...rest] = entity.split('/');
141
+ const entityId = rest.join('/') || 'me';
142
+ const entry = await db.knowledgeEntry.findUnique({
143
+ where: {
144
+ userId_entityType_entityId_key: { userId, entityType, entityId, key },
145
+ },
146
+ });
147
+ if (!entry) {
148
+ return {
149
+ content: [{ type: 'text', text: `No memory found for ${entity}/${key}` }],
150
+ };
151
+ }
152
+ // Update access timestamp
153
+ db.knowledgeEntry.update({
154
+ where: { userId_entityType_entityId_key: { userId, entityType, entityId, key } },
155
+ data: { lastAccessedAt: new Date() },
156
+ }).catch(() => { });
157
+ const valueText = typeof entry.valueRaw === 'string'
158
+ ? entry.valueRaw
159
+ : entry.valueRaw?.text ?? JSON.stringify(entry.valueRaw);
160
+ return {
161
+ content: [{ type: 'text', text: `${entry.valueSummary}\n\n${valueText}` }],
162
+ };
163
+ });
164
+ // iranti_forget — delete a personal memory
165
+ mcpServer.tool('iranti_forget', 'Delete a personal memory from your Iranti knowledge base.', {
166
+ entity: z.string().describe('Entity identifier (e.g. "person/me")'),
167
+ key: z.string().describe('Fact key to forget'),
168
+ }, async ({ entity, key }) => {
169
+ const [entityType, ...rest] = entity.split('/');
170
+ const entityId = rest.join('/') || 'me';
171
+ const deleted = await db.knowledgeEntry.deleteMany({
172
+ where: { userId, entityType, entityId, key },
173
+ });
174
+ if (deleted.count === 0) {
175
+ return {
176
+ content: [{ type: 'text', text: `No memory found for ${entity}/${key} — nothing to forget.` }],
177
+ };
178
+ }
179
+ return {
180
+ content: [{ type: 'text', text: `Forgotten: ${entity}/${key}` }],
181
+ };
182
+ });
183
+ // ── Stateless transport (one per request) ─────────────────────────────────
184
+ const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
185
+ sessionIdGenerator: undefined, // stateless mode
186
+ });
187
+ try {
188
+ await mcpServer.connect(transport);
189
+ await transport.handleRequest(req, res, req.body);
190
+ }
191
+ finally {
192
+ await mcpServer.close();
193
+ }
194
+ });
195
+ return router;
196
+ }
197
+ //# sourceMappingURL=consumerMcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consumerMcp.js","sourceRoot":"","sources":["../../../../src/api/routes/consumerMcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Feedback REST route for iranti (`/feedback`).
3
+ *
4
+ * Receives satisfaction signals submitted by the `iranti feedback` CLI command.
5
+ * Unauthenticated — CLI users do not hold API keys. Rate-limited by IP to
6
+ * discourage abuse. Idempotent on `feedbackId` (client UUID) via upsert, so
7
+ * the CLI offline-retry queue can safely replay submissions.
8
+ *
9
+ * POST /feedback — accept and persist a FeedbackPayload
10
+ */
11
+ import { Router } from 'express';
12
+ export declare const feedbackRouter: Router;
13
+ //# sourceMappingURL=feedback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback.d.ts","sourceRoot":"","sources":["../../../../src/api/routes/feedback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAIpD,eAAO,MAAM,cAAc,EAAE,MAAiB,CAAC"}
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ /**
3
+ * Feedback REST route for iranti (`/feedback`).
4
+ *
5
+ * Receives satisfaction signals submitted by the `iranti feedback` CLI command.
6
+ * Unauthenticated — CLI users do not hold API keys. Rate-limited by IP to
7
+ * discourage abuse. Idempotent on `feedbackId` (client UUID) via upsert, so
8
+ * the CLI offline-retry queue can safely replay submissions.
9
+ *
10
+ * POST /feedback — accept and persist a FeedbackPayload
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.feedbackRouter = void 0;
14
+ const express_1 = require("express");
15
+ const client_1 = require("../../library/client");
16
+ exports.feedbackRouter = (0, express_1.Router)();
17
+ // ── Validation helpers ────────────────────────────────────────────────────────
18
+ const VALID_TYPES = new Set(['bug', 'praise', 'suggestion', 'general']);
19
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20
+ function isValidUuid(v) {
21
+ return typeof v === 'string' && UUID_RE.test(v);
22
+ }
23
+ function isValidRating(v) {
24
+ return typeof v === 'number' && Number.isInteger(v) && v >= 1 && v <= 5;
25
+ }
26
+ function isValidType(v) {
27
+ return typeof v === 'string' && VALID_TYPES.has(v);
28
+ }
29
+ function isNonEmptyString(v) {
30
+ return typeof v === 'string' && v.trim().length > 0;
31
+ }
32
+ function isValidIsoDate(v) {
33
+ if (typeof v !== 'string')
34
+ return false;
35
+ const d = new Date(v);
36
+ return !isNaN(d.getTime());
37
+ }
38
+ // ── POST /feedback ────────────────────────────────────────────────────────────
39
+ exports.feedbackRouter.post('/', async (req, res) => {
40
+ const body = req.body ?? {};
41
+ // Required field validation
42
+ if (!isValidUuid(body.feedbackId)) {
43
+ return res.status(400).json({ error: 'feedbackId must be a valid UUID.' });
44
+ }
45
+ if (!isValidRating(body.rating)) {
46
+ return res.status(400).json({ error: 'rating must be an integer 1–5.' });
47
+ }
48
+ if (!isValidType(body.type)) {
49
+ return res.status(400).json({ error: 'type must be one of: bug, praise, suggestion, general.' });
50
+ }
51
+ if (!isNonEmptyString(body.version)) {
52
+ return res.status(400).json({ error: 'version is required.' });
53
+ }
54
+ if (!isNonEmptyString(body.instanceId)) {
55
+ return res.status(400).json({ error: 'instanceId is required.' });
56
+ }
57
+ if (!isValidIsoDate(body.submittedAt)) {
58
+ return res.status(400).json({ error: 'submittedAt must be a valid ISO 8601 date string.' });
59
+ }
60
+ // Optional field normalisation
61
+ const comment = typeof body.comment === 'string' && body.comment.trim().length > 0
62
+ ? body.comment.trim().slice(0, 2000) // cap at 2000 chars
63
+ : null;
64
+ const os = isNonEmptyString(body.os) ? String(body.os).slice(0, 64) : 'unknown';
65
+ const arch = isNonEmptyString(body.arch) ? String(body.arch).slice(0, 32) : 'unknown';
66
+ const nodeVersion = isNonEmptyString(body.nodeVersion) ? String(body.nodeVersion).slice(0, 32) : 'unknown';
67
+ const sessionCount = typeof body.sessionCount === 'number' && Number.isFinite(body.sessionCount)
68
+ ? Math.max(0, Math.floor(body.sessionCount))
69
+ : null;
70
+ const factCount = typeof body.factCount === 'number' && Number.isFinite(body.factCount)
71
+ ? Math.max(0, Math.floor(body.factCount))
72
+ : null;
73
+ const milestoneContext = typeof body.milestoneContext === 'string' && body.milestoneContext.trim().length > 0
74
+ ? body.milestoneContext.trim().slice(0, 256)
75
+ : null;
76
+ try {
77
+ const db = (0, client_1.getDb)();
78
+ // Upsert on feedbackId — safe for the CLI offline-retry queue.
79
+ // On conflict, update mutable fields only (comment, receivedAt) so
80
+ // repeated drain attempts don't corrupt the original submission data.
81
+ await db.feedback.upsert({
82
+ where: { feedbackId: body.feedbackId },
83
+ create: {
84
+ feedbackId: body.feedbackId,
85
+ rating: body.rating,
86
+ comment,
87
+ type: body.type,
88
+ version: String(body.version).slice(0, 32),
89
+ os,
90
+ arch,
91
+ nodeVersion,
92
+ sessionCount,
93
+ factCount,
94
+ instanceId: String(body.instanceId).slice(0, 64),
95
+ milestoneContext,
96
+ submittedAt: new Date(body.submittedAt),
97
+ },
98
+ update: {
99
+ // Allow comment update in case first attempt sent null
100
+ comment,
101
+ receivedAt: new Date(),
102
+ },
103
+ });
104
+ res.status(200).json({ ok: true });
105
+ }
106
+ catch (err) {
107
+ console.error('[feedback] insert error:', err);
108
+ res.status(500).json({ error: 'Failed to record feedback.' });
109
+ }
110
+ });
111
+ //# sourceMappingURL=feedback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback.js","sourceRoot":"","sources":["../../../../src/api/routes/feedback.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAEH,qCAAoD;AACpD,iDAA6C;AAGhC,QAAA,cAAc,GAAW,IAAA,gBAAM,GAAE,CAAC;AAE/C,iFAAiF;AAEjF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC;AAChF,MAAM,OAAO,GAAG,iEAAiE,CAAC;AAElF,SAAS,WAAW,CAAC,CAAU;IAC3B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,aAAa,CAAC,CAAU;IAC7B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,WAAW,CAAC,CAAU;IAC3B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,SAAS,gBAAgB,CAAC,CAAU;IAChC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,cAAc,CAAC,CAAU;IAC9B,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,iFAAiF;AAEjF,sBAAc,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;IAC3D,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;IAE5B,4BAA4B;IAC5B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAChC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kCAAkC,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gCAAgC,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,wDAAwD,EAAE,CAAC,CAAC;IACrG,CAAC;IACD,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAClC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACrC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QACpC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mDAAmD,EAAE,CAAC,CAAC;IAChG,CAAC;IAED,+BAA+B;IAC/B,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QAC9E,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAG,oBAAoB;QAC3D,CAAC,CAAC,IAAI,CAAC;IAEX,MAAM,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChF,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtF,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE3G,MAAM,YAAY,GAAG,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC;QAC5F,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC5C,CAAC,CAAC,IAAI,CAAC;IAEX,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;QACnF,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,CAAC,CAAC,IAAI,CAAC;IAEX,MAAM,gBAAgB,GAAG,OAAO,IAAI,CAAC,gBAAgB,KAAK,QAAQ,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QACzG,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QAC5C,CAAC,CAAC,IAAI,CAAC;IAEX,IAAI,CAAC;QACD,MAAM,EAAE,GAAG,IAAA,cAAK,GAAE,CAAC;QAEnB,+DAA+D;QAC/D,mEAAmE;QACnE,sEAAsE;QACtE,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YACrB,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE;YACtC,MAAM,EAAE;gBACJ,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,OAAO;gBACP,IAAI,EAAE,IAAI,CAAC,IAAoB;gBAC/B,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;gBAC1C,EAAE;gBACF,IAAI;gBACJ,WAAW;gBACX,YAAY;gBACZ,SAAS;gBACT,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;gBAChD,gBAAgB;gBAChB,WAAW,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;aAC1C;YACD,MAAM,EAAE;gBACJ,uDAAuD;gBACvD,OAAO;gBACP,UAAU,EAAE,IAAI,IAAI,EAAE;aACzB;SACJ,CAAC,CAAC;QAEH,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,GAAG,CAAC,CAAC;QAC/C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC,CAAC;IAClE,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"knowledge.d.ts","sourceRoot":"","sources":["../../../../src/api/routes/knowledge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAgB,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,MAAM,EAA0B,MAAM,WAAW,CAAC;AA+G3D,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA8atD"}
1
+ {"version":3,"file":"knowledge.d.ts","sourceRoot":"","sources":["../../../../src/api/routes/knowledge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAgB,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAC7D,OAAO,EAAE,MAAM,EAA0B,MAAM,WAAW,CAAC;AAmH3D,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA8atD"}
@@ -128,11 +128,15 @@ function fallbackProtocolAgent(req) {
128
128
  : '';
129
129
  if (explicit)
130
130
  return explicit;
131
- const keyId = auth?.keyId?.trim();
132
- // Intentional fallback: protocol state is keyed to the authenticated API key
133
- // when the caller does not send agentId, so protocol-gated reads still have a
134
- // stable principal. Explicit agentId/query aliases still override this path.
135
- return keyId ? `api:${keyId}` : null;
131
+ // Do not fall back to the API key ID as a protocol principal. Protocol
132
+ // enforcement is only meaningful for named agents that have completed a
133
+ // handshake + attend cycle. Using the key ID as a fallback causes any
134
+ // programmatic or diagnostic caller (e.g. the Control Plane health probe)
135
+ // to be blocked with 428 on the first request after an instance restart,
136
+ // since no prior handshake/attend exists for that principal. Callers
137
+ // without an explicit agentId get null here, which causes checkProtocol to
138
+ // return early — no enforcement, no 428.
139
+ return null;
136
140
  }
137
141
  function applyProtocolContext(iranti, req) {
138
142
  const agentId = fallbackProtocolAgent(req);