@semiont/backend 0.4.6 → 0.4.9

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
@@ -268,6 +268,21 @@ var init_jwt = __esm({
268
268
  throw error;
269
269
  }
270
270
  }
271
+ static generateMediaToken(resourceId21, userId14) {
272
+ const payload = { purpose: "media", sub: resourceId21, userId: userId14 };
273
+ return jwt.sign(payload, this.getSecret(), { expiresIn: "5m" });
274
+ }
275
+ static verifyMediaToken(token, resourceId21) {
276
+ let decoded;
277
+ try {
278
+ decoded = jwt.verify(token, this.getSecret());
279
+ } catch (error) {
280
+ if (error instanceof jwt.TokenExpiredError) throw new Error("Media token expired");
281
+ throw new Error("Invalid media token");
282
+ }
283
+ if (decoded["purpose"] !== "media") throw new Error("Invalid media token");
284
+ if (decoded["sub"] !== resourceId21) throw new Error("Media token resource mismatch");
285
+ }
271
286
  static isAllowedDomain(email) {
272
287
  const parts = email.split("@");
273
288
  if (parts.length !== 2 || !parts[0] || !parts[1]) {
@@ -9976,6 +9991,79 @@ function makeMeaningConfigFrom(config2) {
9976
9991
 
9977
9992
  // src/index.ts
9978
9993
  init_logger();
9994
+ var rootRouter = new Hono();
9995
+ rootRouter.get("/", (c) => {
9996
+ const config2 = c.get("config");
9997
+ const projectName = config2._metadata?.projectName ?? "";
9998
+ const projectVersion = config2._metadata?.projectVersion ?? "";
9999
+ const siteName = config2.site?.siteName ?? "";
10000
+ const html = `<!DOCTYPE html>
10001
+ <html lang="en">
10002
+ <head>
10003
+ <meta charset="UTF-8">
10004
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
10005
+ <title>Semiont</title>
10006
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10007
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10008
+ <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap" rel="stylesheet">
10009
+ <style>
10010
+ * { margin: 0; padding: 0; box-sizing: border-box; }
10011
+ body {
10012
+ background: #0a0a0f;
10013
+ color: #e0e0e0;
10014
+ font-family: 'Orbitron', sans-serif;
10015
+ min-height: 100vh;
10016
+ display: flex;
10017
+ flex-direction: column;
10018
+ align-items: center;
10019
+ justify-content: center;
10020
+ gap: 1rem;
10021
+ }
10022
+ h1 {
10023
+ font-size: 4rem;
10024
+ font-weight: 700;
10025
+ letter-spacing: 0.2em;
10026
+ color: #00e5ff;
10027
+ text-transform: uppercase;
10028
+ }
10029
+ h2 {
10030
+ font-size: 1.2rem;
10031
+ font-weight: 400;
10032
+ letter-spacing: 0.4em;
10033
+ color: #888;
10034
+ text-transform: uppercase;
10035
+ }
10036
+ .meta {
10037
+ margin-top: 2rem;
10038
+ font-size: 0.75rem;
10039
+ letter-spacing: 0.15em;
10040
+ color: #555;
10041
+ display: flex;
10042
+ flex-direction: column;
10043
+ align-items: center;
10044
+ gap: 0.3rem;
10045
+ }
10046
+ a {
10047
+ color: #00e5ff;
10048
+ text-decoration: none;
10049
+ letter-spacing: 0.15em;
10050
+ font-size: 0.75rem;
10051
+ }
10052
+ a:hover { text-decoration: underline; }
10053
+ </style>
10054
+ </head>
10055
+ <body>
10056
+ <h1>SEMIONT</h1>
10057
+ <h2>knowledge base</h2>
10058
+ <div class="meta">
10059
+ ${siteName ? `<span>${siteName}</span>` : ""}
10060
+ ${projectName ? `<span>project: ${projectName}${projectVersion ? " v" + projectVersion : ""}</span>` : ""}
10061
+ <a href="/api/health">/api/health</a>
10062
+ </div>
10063
+ </body>
10064
+ </html>`;
10065
+ return c.html(html);
10066
+ });
9979
10067
  var globalForPrisma = global;
9980
10068
  var DatabaseConnection = class {
9981
10069
  static instance = null;
@@ -11542,6 +11630,10 @@ var openapi_default = {
11542
11630
  GatherResourceStreamRequest: {
11543
11631
  type: "object",
11544
11632
  properties: {
11633
+ correlationId: {
11634
+ type: "string",
11635
+ description: "Client-generated correlation ID to thread the response back to the originating request"
11636
+ },
11545
11637
  depth: {
11546
11638
  type: "integer",
11547
11639
  minimum: 1,
@@ -11567,6 +11659,10 @@ var openapi_default = {
11567
11659
  GatherAnnotationStreamRequest: {
11568
11660
  type: "object",
11569
11661
  properties: {
11662
+ correlationId: {
11663
+ type: "string",
11664
+ description: "Client-generated correlation ID to thread the response back to the originating request"
11665
+ },
11570
11666
  contextWindow: {
11571
11667
  type: "integer",
11572
11668
  minimum: 100,
@@ -13392,6 +13488,9 @@ var openapi_default = {
13392
13488
  isAdmin: {
13393
13489
  type: "boolean"
13394
13490
  },
13491
+ isModerator: {
13492
+ type: "boolean"
13493
+ },
13395
13494
  isActive: {
13396
13495
  type: "boolean"
13397
13496
  },
@@ -13419,6 +13518,7 @@ var openapi_default = {
13419
13518
  "domain",
13420
13519
  "provider",
13421
13520
  "isAdmin",
13521
+ "isModerator",
13422
13522
  "isActive",
13423
13523
  "termsAcceptedAt",
13424
13524
  "lastLogin",
@@ -13490,6 +13590,30 @@ var openapi_default = {
13490
13590
  ],
13491
13591
  description: "W3C Web Annotation body purpose vocabulary - https://www.w3.org/TR/annotation-vocab/#motivation"
13492
13592
  },
13593
+ MediaTokenRequest: {
13594
+ type: "object",
13595
+ properties: {
13596
+ resourceId: {
13597
+ type: "string",
13598
+ description: "The resource ID to generate a media token for"
13599
+ }
13600
+ },
13601
+ required: [
13602
+ "resourceId"
13603
+ ]
13604
+ },
13605
+ MediaTokenResponse: {
13606
+ type: "object",
13607
+ properties: {
13608
+ token: {
13609
+ type: "string",
13610
+ description: "Short-lived media token for use as ?token= query parameter on resource URLs"
13611
+ }
13612
+ },
13613
+ required: [
13614
+ "token"
13615
+ ]
13616
+ },
13493
13617
  GatheredContext: {
13494
13618
  type: "object",
13495
13619
  description: "Context gathered for an annotation. Includes source document excerpts, metadata, and graph-derived context. Used by both Find (bind) and Generate (yield) flows.",
@@ -13633,6 +13757,37 @@ var openapi_default = {
13633
13757
  "annotation",
13634
13758
  "sourceResource"
13635
13759
  ]
13760
+ },
13761
+ MatchSearchStreamRequest: {
13762
+ type: "object",
13763
+ properties: {
13764
+ correlationId: {
13765
+ type: "string",
13766
+ description: "Client-generated correlation ID to thread the response back to the originating request"
13767
+ },
13768
+ referenceId: {
13769
+ type: "string",
13770
+ description: "Annotation ID of the reference to search candidates for"
13771
+ },
13772
+ context: {
13773
+ $ref: "#/components/schemas/GatheredContext",
13774
+ description: "Gathered context for the reference annotation"
13775
+ },
13776
+ limit: {
13777
+ type: "integer",
13778
+ minimum: 1,
13779
+ maximum: 20,
13780
+ description: "Maximum number of candidate results to return (default: 10)"
13781
+ },
13782
+ useSemanticScoring: {
13783
+ type: "boolean",
13784
+ description: "Enable semantic similarity scoring in addition to keyword matching (default: true)"
13785
+ }
13786
+ },
13787
+ required: [
13788
+ "referenceId",
13789
+ "context"
13790
+ ]
13636
13791
  }
13637
13792
  }
13638
13793
  }
@@ -13860,8 +14015,33 @@ var OAuthService = class {
13860
14015
  return user;
13861
14016
  }
13862
14017
  };
14018
+
14019
+ // src/middleware/auth.ts
14020
+ init_jwt();
14021
+ var MEDIA_TOKEN_PATH = /^\/api\/resources\/([^/]+)$/;
13863
14022
  var authMiddleware = async (c, next) => {
13864
14023
  const logger2 = c.get("logger");
14024
+ if (c.req.method === "GET") {
14025
+ const mediaTokenParam = c.req.query("token");
14026
+ const match = c.req.path.match(MEDIA_TOKEN_PATH);
14027
+ const resourceId21 = match?.[1];
14028
+ if (mediaTokenParam && resourceId21) {
14029
+ try {
14030
+ JWTService.verifyMediaToken(mediaTokenParam, resourceId21);
14031
+ c.set("token", mediaTokenParam);
14032
+ await next();
14033
+ return;
14034
+ } catch (error) {
14035
+ logger2.warn("Authentication failed: Invalid media token", {
14036
+ type: "auth_failed",
14037
+ reason: "invalid_media_token",
14038
+ path: c.req.path,
14039
+ error: error instanceof Error ? error.message : String(error)
14040
+ });
14041
+ return c.json({ error: "Unauthorized" }, 401);
14042
+ }
14043
+ }
14044
+ }
13865
14045
  const authHeader = c.req.header("Authorization");
13866
14046
  let tokenStr;
13867
14047
  if (authHeader?.startsWith("Bearer ")) {
@@ -14195,6 +14375,7 @@ authRouter.get("/api/users/me", authMiddleware, async (c) => {
14195
14375
  domain: user.domain,
14196
14376
  provider: user.provider,
14197
14377
  isAdmin: user.isAdmin,
14378
+ isModerator: user.isModerator,
14198
14379
  isActive: user.isActive,
14199
14380
  termsAcceptedAt: user.termsAcceptedAt?.toISOString() || null,
14200
14381
  lastLogin: user.lastLogin?.toISOString() || null,
@@ -14260,6 +14441,20 @@ authRouter.post("/api/tokens/mcp-generate", authMiddleware, async (c) => {
14260
14441
  return c.json({ error: "Failed to generate refresh token" }, 401);
14261
14442
  }
14262
14443
  });
14444
+ authRouter.post("/api/tokens/media", authMiddleware, async (c) => {
14445
+ const user = c.get("user");
14446
+ let body;
14447
+ try {
14448
+ body = await c.req.json();
14449
+ } catch {
14450
+ return c.json({ error: "Invalid request body" }, 400);
14451
+ }
14452
+ if (!body.resourceId || typeof body.resourceId !== "string") {
14453
+ return c.json({ error: "resourceId is required" }, 400);
14454
+ }
14455
+ const token = JWTService.generateMediaToken(body.resourceId, user.id);
14456
+ return c.json({ token }, 200);
14457
+ });
14263
14458
  authRouter.post("/api/users/accept-terms", authMiddleware, async (c) => {
14264
14459
  const user = c.get("user");
14265
14460
  await OAuthService.acceptTerms(userId(user.id));
@@ -14753,7 +14948,7 @@ function registerCreateResource(router) {
14753
14948
  const arrayBuffer = await file.arrayBuffer();
14754
14949
  const contentBuffer = Buffer.from(arrayBuffer);
14755
14950
  const eventBus2 = c.get("eventBus");
14756
- const resourceId20 = await ResourceOperations.createResource(
14951
+ const resourceId21 = await ResourceOperations.createResource(
14757
14952
  {
14758
14953
  name,
14759
14954
  content: contentBuffer,
@@ -14766,7 +14961,7 @@ function registerCreateResource(router) {
14766
14961
  userId(userToDid(user)),
14767
14962
  eventBus2
14768
14963
  );
14769
- return c.json({ resourceId: resourceId20 }, 202);
14964
+ return c.json({ resourceId: resourceId21 }, 202);
14770
14965
  });
14771
14966
  }
14772
14967
 
@@ -15126,7 +15321,7 @@ var nanoid = (size = 21) => {
15126
15321
  async function writeTypedSSE(stream, options) {
15127
15322
  await stream.writeSSE({
15128
15323
  event: options.event,
15129
- data: options.data,
15324
+ data: JSON.stringify(options.data),
15130
15325
  id: options.id
15131
15326
  });
15132
15327
  }
@@ -15210,16 +15405,13 @@ function registerAnnotateReferencesStream(router, jobQueue) {
15210
15405
  logger2.info("[EventBus] Received mark:progress event", { progress, resourceId: id });
15211
15406
  try {
15212
15407
  await writeTypedSSE(stream, {
15213
- data: JSON.stringify({
15408
+ data: {
15214
15409
  status: progress.status || "scanning",
15215
15410
  resourceId: resourceId(id),
15216
15411
  currentEntityType: progress.currentEntityType,
15217
- totalEntityTypes: entityTypes.length,
15218
- processedEntityTypes: progress.completedEntityTypes?.length || 0,
15219
- foundCount: progress.completedEntityTypes?.reduce((sum, et) => sum + et.foundCount, 0),
15220
- message: progress.message || (progress.currentEntityType ? `Scanning for ${progress.currentEntityType}...` : "Processing..."),
15221
- percentage: progress.percentage
15222
- }),
15412
+ percentage: progress.percentage,
15413
+ message: progress.message || (progress.currentEntityType ? `Scanning for ${progress.currentEntityType}...` : "Processing...")
15414
+ },
15223
15415
  event: "mark:progress",
15224
15416
  id: String(Date.now())
15225
15417
  });
@@ -15237,15 +15429,13 @@ function registerAnnotateReferencesStream(router, jobQueue) {
15237
15429
  try {
15238
15430
  const result = event.payload.result;
15239
15431
  await writeTypedSSE(stream, {
15240
- data: JSON.stringify({
15432
+ data: {
15241
15433
  motivation: "linking",
15242
15434
  status: "complete",
15243
15435
  resourceId: resourceId(id),
15244
- totalEntityTypes: entityTypes.length,
15245
- processedEntityTypes: entityTypes.length,
15246
15436
  foundCount: result?.totalFound,
15247
15437
  message: result?.totalFound !== void 0 ? `Detection complete! Found ${result.totalFound} entities` : "Detection complete!"
15248
- }),
15438
+ },
15249
15439
  event: "mark:assist-finished",
15250
15440
  id: String(Date.now())
15251
15441
  });
@@ -15262,13 +15452,10 @@ function registerAnnotateReferencesStream(router, jobQueue) {
15262
15452
  logger2.info("Detection failed", { error: event.payload.error });
15263
15453
  try {
15264
15454
  await writeTypedSSE(stream, {
15265
- data: JSON.stringify({
15266
- status: "error",
15455
+ data: {
15267
15456
  resourceId: resourceId(id),
15268
- totalEntityTypes: entityTypes.length,
15269
- processedEntityTypes: 0,
15270
15457
  message: event.payload.error || "Detection failed"
15271
- }),
15458
+ },
15272
15459
  event: "mark:assist-failed",
15273
15460
  id: String(Date.now())
15274
15461
  });
@@ -15398,11 +15585,11 @@ function registerAnnotateHighlightsStream(router, jobQueue) {
15398
15585
  logger2.info("Detection started");
15399
15586
  try {
15400
15587
  await writeTypedSSE(stream, {
15401
- data: JSON.stringify({
15588
+ data: {
15402
15589
  status: "started",
15403
15590
  resourceId: resourceId(id),
15404
15591
  message: "Starting detection..."
15405
- }),
15592
+ },
15406
15593
  event: "mark:progress",
15407
15594
  id: String(Date.now())
15408
15595
  });
@@ -15418,13 +15605,12 @@ function registerAnnotateHighlightsStream(router, jobQueue) {
15418
15605
  logger2.info("Detection progress", { progress });
15419
15606
  try {
15420
15607
  await writeTypedSSE(stream, {
15421
- data: JSON.stringify({
15608
+ data: {
15422
15609
  status: progress.status || "analyzing",
15423
15610
  resourceId: resourceId(id),
15424
- stage: progress.status === "analyzing" || progress.status === "creating" ? progress.status : void 0,
15425
15611
  percentage: progress.percentage,
15426
15612
  message: progress.message || "Processing..."
15427
- }),
15613
+ },
15428
15614
  event: "mark:progress",
15429
15615
  id: String(Date.now())
15430
15616
  });
@@ -15442,7 +15628,7 @@ function registerAnnotateHighlightsStream(router, jobQueue) {
15442
15628
  try {
15443
15629
  const result = event.payload.result;
15444
15630
  await writeTypedSSE(stream, {
15445
- data: JSON.stringify({
15631
+ data: {
15446
15632
  motivation: "highlighting",
15447
15633
  status: "complete",
15448
15634
  resourceId: resourceId(id),
@@ -15450,7 +15636,7 @@ function registerAnnotateHighlightsStream(router, jobQueue) {
15450
15636
  foundCount: result?.highlightsFound,
15451
15637
  createdCount: result?.highlightsCreated,
15452
15638
  message: result?.highlightsCreated !== void 0 ? `Complete! Created ${result.highlightsCreated} highlights` : "Highlight detection complete!"
15453
- }),
15639
+ },
15454
15640
  event: "mark:assist-finished",
15455
15641
  id: String(Date.now())
15456
15642
  });
@@ -15467,11 +15653,10 @@ function registerAnnotateHighlightsStream(router, jobQueue) {
15467
15653
  logger2.info("Detection failed", { error: event.payload.error });
15468
15654
  try {
15469
15655
  await writeTypedSSE(stream, {
15470
- data: JSON.stringify({
15471
- status: "error",
15656
+ data: {
15472
15657
  resourceId: resourceId(id),
15473
15658
  message: event.payload.error || "Highlight detection failed"
15474
- }),
15659
+ },
15475
15660
  event: "mark:assist-failed",
15476
15661
  id: String(Date.now())
15477
15662
  });
@@ -15601,11 +15786,11 @@ function registerAnnotateAssessmentsStream(router, jobQueue) {
15601
15786
  logger2.info("Detection started");
15602
15787
  try {
15603
15788
  await writeTypedSSE(stream, {
15604
- data: JSON.stringify({
15789
+ data: {
15605
15790
  status: "started",
15606
15791
  resourceId: resourceId(id),
15607
15792
  message: "Starting detection..."
15608
- }),
15793
+ },
15609
15794
  event: "mark:progress",
15610
15795
  id: String(Date.now())
15611
15796
  });
@@ -15621,13 +15806,12 @@ function registerAnnotateAssessmentsStream(router, jobQueue) {
15621
15806
  logger2.info("Detection progress", { progress });
15622
15807
  try {
15623
15808
  await writeTypedSSE(stream, {
15624
- data: JSON.stringify({
15809
+ data: {
15625
15810
  status: progress.status || "analyzing",
15626
15811
  resourceId: resourceId(id),
15627
- stage: progress.status === "analyzing" || progress.status === "creating" ? progress.status : void 0,
15628
15812
  percentage: progress.percentage,
15629
15813
  message: progress.message || "Processing..."
15630
- }),
15814
+ },
15631
15815
  event: "mark:progress",
15632
15816
  id: String(Date.now())
15633
15817
  });
@@ -15649,7 +15833,7 @@ function registerAnnotateAssessmentsStream(router, jobQueue) {
15649
15833
  try {
15650
15834
  const result = event.payload.result;
15651
15835
  await writeTypedSSE(stream, {
15652
- data: JSON.stringify({
15836
+ data: {
15653
15837
  motivation: "assessing",
15654
15838
  status: "complete",
15655
15839
  resourceId: resourceId(id),
@@ -15657,7 +15841,7 @@ function registerAnnotateAssessmentsStream(router, jobQueue) {
15657
15841
  foundCount: result?.assessmentsFound,
15658
15842
  createdCount: result?.assessmentsCreated,
15659
15843
  message: result?.assessmentsCreated !== void 0 ? `Complete! Created ${result.assessmentsCreated} assessments` : "Assessment detection complete!"
15660
- }),
15844
+ },
15661
15845
  event: "mark:assist-finished",
15662
15846
  id: String(Date.now())
15663
15847
  });
@@ -15674,11 +15858,10 @@ function registerAnnotateAssessmentsStream(router, jobQueue) {
15674
15858
  logger2.info("Detection failed", { error: event.payload.error });
15675
15859
  try {
15676
15860
  await writeTypedSSE(stream, {
15677
- data: JSON.stringify({
15678
- status: "error",
15861
+ data: {
15679
15862
  resourceId: resourceId(id),
15680
15863
  message: event.payload.error || "Assessment detection failed"
15681
- }),
15864
+ },
15682
15865
  event: "mark:assist-failed",
15683
15866
  id: String(Date.now())
15684
15867
  });
@@ -15808,11 +15991,11 @@ function registerAnnotateCommentsStream(router, jobQueue) {
15808
15991
  logger2.info("Detection started");
15809
15992
  try {
15810
15993
  await writeTypedSSE(stream, {
15811
- data: JSON.stringify({
15994
+ data: {
15812
15995
  status: "started",
15813
15996
  resourceId: resourceId(id),
15814
15997
  message: "Starting detection..."
15815
- }),
15998
+ },
15816
15999
  event: "mark:progress",
15817
16000
  id: String(Date.now())
15818
16001
  });
@@ -15828,13 +16011,12 @@ function registerAnnotateCommentsStream(router, jobQueue) {
15828
16011
  logger2.info("Detection progress", { progress });
15829
16012
  try {
15830
16013
  await writeTypedSSE(stream, {
15831
- data: JSON.stringify({
16014
+ data: {
15832
16015
  status: progress.status || "analyzing",
15833
16016
  resourceId: resourceId(id),
15834
- stage: progress.status === "analyzing" || progress.status === "creating" ? progress.status : void 0,
15835
16017
  percentage: progress.percentage,
15836
16018
  message: progress.message || "Processing..."
15837
- }),
16019
+ },
15838
16020
  event: "mark:progress",
15839
16021
  id: String(Date.now())
15840
16022
  });
@@ -15852,7 +16034,7 @@ function registerAnnotateCommentsStream(router, jobQueue) {
15852
16034
  try {
15853
16035
  const result = event.payload.result;
15854
16036
  await writeTypedSSE(stream, {
15855
- data: JSON.stringify({
16037
+ data: {
15856
16038
  motivation: "commenting",
15857
16039
  status: "complete",
15858
16040
  resourceId: resourceId(id),
@@ -15860,7 +16042,7 @@ function registerAnnotateCommentsStream(router, jobQueue) {
15860
16042
  foundCount: result?.commentsFound,
15861
16043
  createdCount: result?.commentsCreated,
15862
16044
  message: result?.commentsCreated !== void 0 ? `Complete! Created ${result.commentsCreated} comments` : "Comment detection complete!"
15863
- }),
16045
+ },
15864
16046
  event: "mark:assist-finished",
15865
16047
  id: String(Date.now())
15866
16048
  });
@@ -15877,11 +16059,10 @@ function registerAnnotateCommentsStream(router, jobQueue) {
15877
16059
  logger2.info("Detection failed", { error: event.payload.error });
15878
16060
  try {
15879
16061
  await writeTypedSSE(stream, {
15880
- data: JSON.stringify({
15881
- status: "error",
16062
+ data: {
15882
16063
  resourceId: resourceId(id),
15883
16064
  message: event.payload.error || "Comment detection failed"
15884
- }),
16065
+ },
15885
16066
  event: "mark:assist-failed",
15886
16067
  id: String(Date.now())
15887
16068
  });
@@ -16185,12 +16366,12 @@ function registerAnnotateTagsStream(router, jobQueue) {
16185
16366
  logger2.info("Detection started");
16186
16367
  try {
16187
16368
  await writeTypedSSE(stream, {
16188
- data: JSON.stringify({
16369
+ data: {
16189
16370
  status: "started",
16190
16371
  resourceId: resourceId(id),
16191
16372
  totalCategories: categories.length,
16192
16373
  message: "Starting detection..."
16193
- }),
16374
+ },
16194
16375
  event: "mark:progress",
16195
16376
  id: String(Date.now())
16196
16377
  });
@@ -16206,16 +16387,15 @@ function registerAnnotateTagsStream(router, jobQueue) {
16206
16387
  logger2.info("Detection progress", { progress });
16207
16388
  try {
16208
16389
  await writeTypedSSE(stream, {
16209
- data: JSON.stringify({
16390
+ data: {
16210
16391
  status: progress.status || "analyzing",
16211
16392
  resourceId: resourceId(id),
16212
- stage: progress.status === "analyzing" || progress.status === "creating" ? progress.status : void 0,
16213
16393
  percentage: progress.percentage,
16214
16394
  currentCategory: progress.currentCategory,
16215
16395
  processedCategories: progress.processedCategories,
16216
16396
  totalCategories: progress.totalCategories,
16217
16397
  message: progress.message || "Processing..."
16218
- }),
16398
+ },
16219
16399
  event: "mark:progress",
16220
16400
  id: String(Date.now())
16221
16401
  });
@@ -16233,7 +16413,7 @@ function registerAnnotateTagsStream(router, jobQueue) {
16233
16413
  try {
16234
16414
  const result = event.payload.result;
16235
16415
  await writeTypedSSE(stream, {
16236
- data: JSON.stringify({
16416
+ data: {
16237
16417
  motivation: "tagging",
16238
16418
  status: "complete",
16239
16419
  resourceId: resourceId(id),
@@ -16242,7 +16422,7 @@ function registerAnnotateTagsStream(router, jobQueue) {
16242
16422
  createdCount: result?.tagsCreated,
16243
16423
  byCategory: result?.byCategory,
16244
16424
  message: result?.tagsCreated !== void 0 ? `Complete! Created ${result.tagsCreated} tags` : "Tag detection complete!"
16245
- }),
16425
+ },
16246
16426
  event: "mark:assist-finished",
16247
16427
  id: String(Date.now())
16248
16428
  });
@@ -16259,11 +16439,10 @@ function registerAnnotateTagsStream(router, jobQueue) {
16259
16439
  logger2.info("Detection failed", { error: event.payload.error });
16260
16440
  try {
16261
16441
  await writeTypedSSE(stream, {
16262
- data: JSON.stringify({
16263
- status: "error",
16442
+ data: {
16264
16443
  resourceId: resourceId(id),
16265
16444
  message: event.payload.error || "Tag detection failed"
16266
- }),
16445
+ },
16267
16446
  event: "mark:assist-failed",
16268
16447
  id: String(Date.now())
16269
16448
  });
@@ -16337,118 +16516,122 @@ function registerGetReferencedBy(router) {
16337
16516
  });
16338
16517
  }
16339
16518
  init_logger();
16340
- function registerBindSearchStream(router) {
16341
- router.post("/resources/:id/bind-search-stream", async (c) => {
16342
- const { id } = c.req.param();
16343
- const logger2 = getLogger().child({
16344
- component: "bind-search-stream",
16345
- resourceId: id
16346
- });
16347
- const user = c.get("user");
16348
- if (!user) {
16349
- throw new HTTPException(401, { message: "Authentication required" });
16350
- }
16351
- const body = await c.req.json();
16352
- const { referenceId, context, limit, useSemanticScoring } = body;
16353
- if (!referenceId || !context) {
16354
- throw new HTTPException(400, { message: "referenceId and context are required" });
16355
- }
16356
- const eventBus2 = c.get("eventBus");
16357
- const { knowledgeSystem: { kb } } = c.get("makeMeaning");
16358
- const resource = await ResourceContext.getResourceMetadata(resourceId(id), kb);
16359
- if (!resource) {
16360
- throw new HTTPException(404, { message: "Resource not found" });
16361
- }
16362
- const correlationId = crypto.randomUUID();
16363
- logger2.info("Starting bind search stream", { referenceId, correlationId });
16364
- c.header("X-Accel-Buffering", "no");
16365
- c.header("Cache-Control", "no-cache, no-transform");
16366
- return streamSSE(c, async (stream) => {
16367
- let isStreamClosed = false;
16368
- const subscriptions = [];
16369
- let closeStreamCallback = null;
16370
- const streamPromise = new Promise((resolve) => {
16371
- closeStreamCallback = resolve;
16519
+ function registerMatchSearchStream(router) {
16520
+ router.post(
16521
+ "/resources/:id/match-search-stream",
16522
+ validateRequestBody("MatchSearchStreamRequest"),
16523
+ async (c) => {
16524
+ const { id } = c.req.param();
16525
+ const logger2 = getLogger().child({
16526
+ component: "match-search-stream",
16527
+ resourceId: id
16372
16528
  });
16373
- const cleanup = () => {
16374
- if (isStreamClosed) return;
16375
- isStreamClosed = true;
16376
- subscriptions.forEach((sub) => sub.unsubscribe());
16377
- if (closeStreamCallback) closeStreamCallback();
16378
- };
16379
- try {
16380
- subscriptions.push(
16381
- eventBus2.get("match:search-results").subscribe(async (event) => {
16382
- if (event.correlationId !== correlationId) return;
16383
- if (isStreamClosed) return;
16384
- logger2.info("Bind search completed", {
16385
- referenceId,
16386
- resultCount: event.results.length
16387
- });
16388
- try {
16389
- await writeTypedSSE(stream, {
16390
- data: JSON.stringify({
16391
- referenceId: event.referenceId,
16392
- results: event.results
16393
- }),
16394
- event: "match:search-results",
16395
- id: String(Date.now())
16396
- });
16397
- } catch (error) {
16398
- logger2.warn("Client disconnected during results");
16399
- }
16400
- cleanup();
16401
- })
16402
- );
16403
- subscriptions.push(
16404
- eventBus2.get("match:search-failed").subscribe(async (event) => {
16405
- if (event.correlationId !== correlationId) return;
16406
- if (isStreamClosed) return;
16407
- logger2.error("Bind search failed", { referenceId, error: event.error });
16408
- try {
16409
- await writeTypedSSE(stream, {
16410
- data: JSON.stringify({
16411
- referenceId: event.referenceId,
16412
- error: event.error.message
16413
- }),
16414
- event: "match:search-failed",
16415
- id: String(Date.now())
16416
- });
16417
- } catch (error) {
16418
- logger2.warn("Client disconnected during error");
16419
- }
16420
- cleanup();
16421
- })
16422
- );
16423
- eventBus2.get("match:search-requested").next({
16424
- correlationId,
16425
- referenceId,
16426
- context,
16427
- limit,
16428
- useSemanticScoring
16429
- });
16430
- c.req.raw.signal.addEventListener("abort", () => {
16431
- logger2.info("Client disconnected from bind search stream");
16432
- cleanup();
16529
+ const user = c.get("user");
16530
+ if (!user) {
16531
+ throw new HTTPException(401, { message: "Authentication required" });
16532
+ }
16533
+ const body = c.get("validatedBody");
16534
+ const { referenceId, context, limit, useSemanticScoring } = body;
16535
+ const correlationId = body.correlationId ?? crypto.randomUUID();
16536
+ const eventBus2 = c.get("eventBus");
16537
+ const { knowledgeSystem: { kb } } = c.get("makeMeaning");
16538
+ const resource = await ResourceContext.getResourceMetadata(resourceId(id), kb);
16539
+ if (!resource) {
16540
+ throw new HTTPException(404, { message: "Resource not found" });
16541
+ }
16542
+ logger2.info("Starting match search stream", { referenceId, correlationId });
16543
+ c.header("X-Accel-Buffering", "no");
16544
+ c.header("Cache-Control", "no-cache, no-transform");
16545
+ return streamSSE(c, async (stream) => {
16546
+ let isStreamClosed = false;
16547
+ const subscriptions = [];
16548
+ let closeStreamCallback = null;
16549
+ const streamPromise = new Promise((resolve) => {
16550
+ closeStreamCallback = resolve;
16433
16551
  });
16434
- } catch (error) {
16552
+ const cleanup = () => {
16553
+ if (isStreamClosed) return;
16554
+ isStreamClosed = true;
16555
+ subscriptions.forEach((sub) => sub.unsubscribe());
16556
+ if (closeStreamCallback) closeStreamCallback();
16557
+ };
16435
16558
  try {
16436
- await writeTypedSSE(stream, {
16437
- data: JSON.stringify({
16438
- referenceId,
16439
- error: error instanceof Error ? error.message : "Search failed"
16440
- }),
16441
- event: "match:search-failed",
16442
- id: String(Date.now())
16559
+ subscriptions.push(
16560
+ eventBus2.get("match:search-results").subscribe(async (event) => {
16561
+ if (event.correlationId !== correlationId) return;
16562
+ if (isStreamClosed) return;
16563
+ logger2.info("Match search completed", {
16564
+ referenceId,
16565
+ resultCount: event.response.length
16566
+ });
16567
+ try {
16568
+ await writeTypedSSE(stream, {
16569
+ data: {
16570
+ correlationId: event.correlationId,
16571
+ referenceId: event.referenceId,
16572
+ response: event.response
16573
+ },
16574
+ event: "match:search-results",
16575
+ id: String(Date.now())
16576
+ });
16577
+ } catch {
16578
+ logger2.warn("Client disconnected during results");
16579
+ }
16580
+ cleanup();
16581
+ })
16582
+ );
16583
+ subscriptions.push(
16584
+ eventBus2.get("match:search-failed").subscribe(async (event) => {
16585
+ if (event.correlationId !== correlationId) return;
16586
+ if (isStreamClosed) return;
16587
+ logger2.error("Match search failed", { referenceId, error: event.error });
16588
+ try {
16589
+ await writeTypedSSE(stream, {
16590
+ data: {
16591
+ correlationId: event.correlationId,
16592
+ referenceId: event.referenceId,
16593
+ error: event.error
16594
+ },
16595
+ event: "match:search-failed",
16596
+ id: String(Date.now())
16597
+ });
16598
+ } catch {
16599
+ logger2.warn("Client disconnected during error");
16600
+ }
16601
+ cleanup();
16602
+ })
16603
+ );
16604
+ eventBus2.get("match:search-requested").next({
16605
+ correlationId,
16606
+ referenceId,
16607
+ context,
16608
+ limit,
16609
+ useSemanticScoring
16443
16610
  });
16444
- } catch (sseError) {
16445
- logger2.warn("Could not send error to client");
16611
+ c.req.raw.signal.addEventListener("abort", () => {
16612
+ logger2.info("Client disconnected from match search stream");
16613
+ cleanup();
16614
+ });
16615
+ } catch (error) {
16616
+ try {
16617
+ await writeTypedSSE(stream, {
16618
+ data: {
16619
+ correlationId,
16620
+ referenceId,
16621
+ error: error instanceof Error ? error.message : "Search failed"
16622
+ },
16623
+ event: "match:search-failed",
16624
+ id: String(Date.now())
16625
+ });
16626
+ } catch {
16627
+ logger2.warn("Could not send error to client");
16628
+ }
16629
+ cleanup();
16446
16630
  }
16447
- cleanup();
16448
- }
16449
- return streamPromise;
16450
- });
16451
- });
16631
+ return streamPromise;
16632
+ });
16633
+ }
16634
+ );
16452
16635
  }
16453
16636
  function registerTokenRoutes(router) {
16454
16637
  router.get("/api/clone-tokens/:token", async (c) => {
@@ -16928,13 +17111,13 @@ function registerYieldResourceStream(router, jobQueue) {
16928
17111
  logger2.info("Generation started");
16929
17112
  try {
16930
17113
  await writeTypedSSE(stream, {
16931
- data: JSON.stringify({
17114
+ data: {
16932
17115
  status: "started",
16933
17116
  referenceId: reference.id,
16934
17117
  resourceName,
16935
17118
  percentage: 0,
16936
17119
  message: "Starting..."
16937
- }),
17120
+ },
16938
17121
  event: "yield:progress",
16939
17122
  id: String(Date.now())
16940
17123
  });
@@ -16950,13 +17133,13 @@ function registerYieldResourceStream(router, jobQueue) {
16950
17133
  logger2.info("Generation progress", { progress });
16951
17134
  try {
16952
17135
  await writeTypedSSE(stream, {
16953
- data: JSON.stringify({
17136
+ data: {
16954
17137
  status: progress.status,
16955
17138
  referenceId: reference.id,
16956
17139
  resourceName,
16957
17140
  percentage: progress.percentage || 0,
16958
17141
  message: progress.message || `${progress.status}...`
16959
- }),
17142
+ },
16960
17143
  event: "yield:progress",
16961
17144
  id: String(Date.now())
16962
17145
  });
@@ -16972,7 +17155,7 @@ function registerYieldResourceStream(router, jobQueue) {
16972
17155
  logger2.info("Generation completed");
16973
17156
  try {
16974
17157
  await writeTypedSSE(stream, {
16975
- data: JSON.stringify({
17158
+ data: {
16976
17159
  status: "complete",
16977
17160
  referenceId: reference.id,
16978
17161
  resourceName,
@@ -16980,7 +17163,7 @@ function registerYieldResourceStream(router, jobQueue) {
16980
17163
  sourceResourceId: resourceIdParam,
16981
17164
  percentage: 100,
16982
17165
  message: "Draft resource created! Ready for review."
16983
- }),
17166
+ },
16984
17167
  event: "yield:finished",
16985
17168
  id: String(Date.now())
16986
17169
  });
@@ -17012,12 +17195,12 @@ function registerYieldResourceStream(router, jobQueue) {
17012
17195
  } catch (error) {
17013
17196
  try {
17014
17197
  await writeTypedSSE(stream, {
17015
- data: JSON.stringify({
17198
+ data: {
17016
17199
  status: "error",
17017
17200
  referenceId: reference.id,
17018
17201
  percentage: 0,
17019
17202
  message: error instanceof Error ? error.message : "Generation failed"
17020
- }),
17203
+ },
17021
17204
  event: "yield:failed",
17022
17205
  id: String(Date.now())
17023
17206
  });
@@ -17050,12 +17233,7 @@ function registerGatherAnnotationStream(router) {
17050
17233
  if (!user) {
17051
17234
  throw new HTTPException(401, { message: "Authentication required" });
17052
17235
  }
17053
- eventBus2.get("gather:requested").next({
17054
- annotationId: annotationId(annotationIdParam),
17055
- resourceId: resourceId(resourceIdParam),
17056
- options: { includeSourceContext: true, includeTargetContext: true, contextWindow }
17057
- });
17058
- logger2.info("Emitted gather:requested", { annotationId: annotationIdParam, contextWindow });
17236
+ const correlationId = body.correlationId ?? crypto.randomUUID();
17059
17237
  c.header("X-Accel-Buffering", "no");
17060
17238
  c.header("Cache-Control", "no-cache, no-transform");
17061
17239
  return streamSSE(c, async (stream) => {
@@ -17075,7 +17253,7 @@ function registerGatherAnnotationStream(router) {
17075
17253
  if (isStreamClosed) return;
17076
17254
  try {
17077
17255
  await writeTypedSSE(stream, {
17078
- data: JSON.stringify({ message: event.message, percentage: event.percentage }),
17256
+ data: { message: event.message, percentage: event.percentage },
17079
17257
  event: "gather:annotation-progress",
17080
17258
  id: String(Date.now())
17081
17259
  });
@@ -17086,11 +17264,11 @@ function registerGatherAnnotationStream(router) {
17086
17264
  );
17087
17265
  subscriptions.push(
17088
17266
  eventBus2.get("gather:complete").subscribe(async (event) => {
17089
- if (event.annotationId !== annotationIdParam) return;
17267
+ if (event.correlationId !== correlationId) return;
17090
17268
  if (isStreamClosed) return;
17091
17269
  try {
17092
17270
  await writeTypedSSE(stream, {
17093
- data: JSON.stringify({ annotationId: event.annotationId, response: event.response }),
17271
+ data: { correlationId: event.correlationId, annotationId: event.annotationId, response: event.response },
17094
17272
  event: "gather:annotation-finished",
17095
17273
  id: String(Date.now())
17096
17274
  });
@@ -17101,11 +17279,11 @@ function registerGatherAnnotationStream(router) {
17101
17279
  );
17102
17280
  subscriptions.push(
17103
17281
  eventBus2.get("gather:failed").subscribe(async (event) => {
17104
- if (event.annotationId !== annotationIdParam) return;
17282
+ if (event.correlationId !== correlationId) return;
17105
17283
  if (isStreamClosed) return;
17106
17284
  try {
17107
17285
  await writeTypedSSE(stream, {
17108
- data: JSON.stringify({ annotationId: event.annotationId, error: event.error }),
17286
+ data: { correlationId: event.correlationId, annotationId: event.annotationId, error: event.error },
17109
17287
  event: "gather:failed",
17110
17288
  id: String(Date.now())
17111
17289
  });
@@ -17114,6 +17292,13 @@ function registerGatherAnnotationStream(router) {
17114
17292
  cleanup();
17115
17293
  })
17116
17294
  );
17295
+ eventBus2.get("gather:requested").next({
17296
+ correlationId,
17297
+ annotationId: annotationId(annotationIdParam),
17298
+ resourceId: resourceId(resourceIdParam),
17299
+ options: { includeSourceContext: true, includeTargetContext: true, contextWindow }
17300
+ });
17301
+ logger2.info("Emitted gather:requested", { annotationId: annotationIdParam, correlationId, contextWindow });
17117
17302
  keepAliveInterval = setInterval(async () => {
17118
17303
  if (isStreamClosed) {
17119
17304
  clearInterval(keepAliveInterval);
@@ -17137,14 +17322,14 @@ function registerGatherAnnotationStream(router) {
17137
17322
  }
17138
17323
  function registerGetAnnotationHistory(router) {
17139
17324
  router.get("/resources/:resourceId/annotations/:annotationId/history", async (c) => {
17140
- const { resourceId: resourceId20, annotationId: annotationId6 } = c.req.param();
17325
+ const { resourceId: resourceId21, annotationId: annotationId7 } = c.req.param();
17141
17326
  const eventBus2 = c.get("eventBus");
17142
17327
  const correlationId = crypto.randomUUID();
17143
17328
  try {
17144
17329
  const response = await eventBusRequest(
17145
17330
  eventBus2,
17146
17331
  "browse:annotation-history-requested",
17147
- { correlationId, resourceId: resourceId(resourceId20), annotationId: annotationId(annotationId6) },
17332
+ { correlationId, resourceId: resourceId(resourceId21), annotationId: annotationId(annotationId7) },
17148
17333
  "browse:annotation-history-result",
17149
17334
  "browse:annotation-history-failed"
17150
17335
  );
@@ -17178,7 +17363,7 @@ function createResourcesRouter(jobQueue) {
17178
17363
  registerAnnotateCommentsStream(resourcesRouter2, jobQueue);
17179
17364
  registerAnnotateTagsStream(resourcesRouter2, jobQueue);
17180
17365
  registerGetReferencedBy(resourcesRouter2);
17181
- registerBindSearchStream(resourcesRouter2);
17366
+ registerMatchSearchStream(resourcesRouter2);
17182
17367
  registerGetResourceAnnotations(resourcesRouter2);
17183
17368
  registerCreateAnnotation(resourcesRouter2);
17184
17369
  registerGetAnnotation(resourcesRouter2);
@@ -17239,8 +17424,8 @@ operationsRouter.get("/api/annotations/:id/context", async (c) => {
17239
17424
  const { id } = c.req.param();
17240
17425
  const query = c.req.query();
17241
17426
  const { knowledgeSystem: { kb } } = c.get("makeMeaning");
17242
- const resourceId20 = query.resourceId;
17243
- if (!resourceId20) {
17427
+ const resourceId21 = query.resourceId;
17428
+ if (!resourceId21) {
17244
17429
  throw new HTTPException(400, { message: "resourceId query parameter is required" });
17245
17430
  }
17246
17431
  const contextBefore = query.contextBefore ? Number(query.contextBefore) : 100;
@@ -17254,7 +17439,7 @@ operationsRouter.get("/api/annotations/:id/context", async (c) => {
17254
17439
  try {
17255
17440
  const response = await AnnotationContext.getAnnotationContext(
17256
17441
  annotationId(id),
17257
- resourceId(resourceId20),
17442
+ resourceId(resourceId21),
17258
17443
  contextBefore,
17259
17444
  contextAfter,
17260
17445
  kb
@@ -17280,14 +17465,14 @@ operationsRouter.get("/api/annotations/:id/summary", async (c) => {
17280
17465
  const { id } = c.req.param();
17281
17466
  const query = c.req.query();
17282
17467
  const { knowledgeSystem: { gatherer } } = c.get("makeMeaning");
17283
- const resourceId20 = query.resourceId;
17284
- if (!resourceId20) {
17468
+ const resourceId21 = query.resourceId;
17469
+ if (!resourceId21) {
17285
17470
  throw new HTTPException(400, { message: "resourceId query parameter is required" });
17286
17471
  }
17287
17472
  try {
17288
17473
  const response = await gatherer.generateAnnotationSummary(
17289
17474
  annotationId(id),
17290
- resourceId(resourceId20)
17475
+ resourceId(resourceId21)
17291
17476
  );
17292
17477
  return c.json(response);
17293
17478
  } catch (error) {
@@ -17774,6 +17959,7 @@ app.use("*", async (c, next) => {
17774
17959
  c.set("makeMeaning", makeMeaning);
17775
17960
  await next();
17776
17961
  });
17962
+ app.route("/", rootRouter);
17777
17963
  app.route("/", healthRouter);
17778
17964
  app.route("/", authRouter);
17779
17965
  app.route("/", statusRouter);