@agent-native/core 0.53.0 → 0.54.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 (106) hide show
  1. package/dist/action.d.ts +40 -1
  2. package/dist/action.d.ts.map +1 -1
  3. package/dist/action.js +69 -2
  4. package/dist/action.js.map +1 -1
  5. package/dist/agent/index.d.ts +1 -0
  6. package/dist/agent/index.d.ts.map +1 -1
  7. package/dist/agent/index.js +1 -0
  8. package/dist/agent/index.js.map +1 -1
  9. package/dist/agent/observational-memory/index.d.ts +6 -6
  10. package/dist/agent/observational-memory/index.js +6 -6
  11. package/dist/agent/observational-memory/index.js.map +1 -1
  12. package/dist/agent/observational-memory/read.d.ts +7 -9
  13. package/dist/agent/observational-memory/read.d.ts.map +1 -1
  14. package/dist/agent/observational-memory/read.js +7 -9
  15. package/dist/agent/observational-memory/read.js.map +1 -1
  16. package/dist/agent/processors.d.ts +146 -0
  17. package/dist/agent/processors.d.ts.map +1 -0
  18. package/dist/agent/processors.js +122 -0
  19. package/dist/agent/processors.js.map +1 -0
  20. package/dist/agent/production-agent.d.ts +10 -0
  21. package/dist/agent/production-agent.d.ts.map +1 -1
  22. package/dist/agent/production-agent.js +101 -0
  23. package/dist/agent/production-agent.js.map +1 -1
  24. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  25. package/dist/agent/run-loop-with-resume.js +4 -5
  26. package/dist/agent/run-loop-with-resume.js.map +1 -1
  27. package/dist/agent/tool-call-journal.d.ts +6 -8
  28. package/dist/agent/tool-call-journal.d.ts.map +1 -1
  29. package/dist/agent/tool-call-journal.js +6 -8
  30. package/dist/agent/tool-call-journal.js.map +1 -1
  31. package/dist/agent/types.d.ts +11 -0
  32. package/dist/agent/types.d.ts.map +1 -1
  33. package/dist/agent/types.js.map +1 -1
  34. package/dist/cli/gateway-helpers.d.ts +15 -0
  35. package/dist/cli/gateway-helpers.d.ts.map +1 -0
  36. package/dist/cli/gateway-helpers.js +51 -0
  37. package/dist/cli/gateway-helpers.js.map +1 -0
  38. package/dist/cli/plan-local.d.ts.map +1 -1
  39. package/dist/cli/plan-local.js +129 -4
  40. package/dist/cli/plan-local.js.map +1 -1
  41. package/dist/cli/skills.d.ts.map +1 -1
  42. package/dist/cli/skills.js +38 -3
  43. package/dist/cli/skills.js.map +1 -1
  44. package/dist/cli/workspace-dev.d.ts.map +1 -1
  45. package/dist/cli/workspace-dev.js +9 -27
  46. package/dist/cli/workspace-dev.js.map +1 -1
  47. package/dist/coding-tools/run-code.d.ts.map +1 -1
  48. package/dist/coding-tools/run-code.js +18 -2
  49. package/dist/coding-tools/run-code.js.map +1 -1
  50. package/dist/extensions/fetch-tool.d.ts.map +1 -1
  51. package/dist/extensions/fetch-tool.js +80 -15
  52. package/dist/extensions/fetch-tool.js.map +1 -1
  53. package/dist/extensions/web-content.d.ts +61 -0
  54. package/dist/extensions/web-content.d.ts.map +1 -0
  55. package/dist/extensions/web-content.js +468 -0
  56. package/dist/extensions/web-content.js.map +1 -0
  57. package/dist/extensions/web-search-tool.js +3 -3
  58. package/dist/extensions/web-search-tool.js.map +1 -1
  59. package/dist/mcp/build-server.d.ts.map +1 -1
  60. package/dist/mcp/build-server.js +4 -1
  61. package/dist/mcp/build-server.js.map +1 -1
  62. package/dist/provider-api/corpus-jobs.d.ts +80 -0
  63. package/dist/provider-api/corpus-jobs.d.ts.map +1 -1
  64. package/dist/provider-api/corpus-jobs.js +219 -22
  65. package/dist/provider-api/corpus-jobs.js.map +1 -1
  66. package/dist/provider-api/index.d.ts +24 -32
  67. package/dist/provider-api/index.d.ts.map +1 -1
  68. package/dist/provider-api/index.js +28 -1
  69. package/dist/provider-api/index.js.map +1 -1
  70. package/dist/server/agent-chat-plugin.js +1 -1
  71. package/dist/server/agent-chat-plugin.js.map +1 -1
  72. package/dist/server/better-auth-instance.d.ts +7 -0
  73. package/dist/server/better-auth-instance.d.ts.map +1 -1
  74. package/dist/server/better-auth-instance.js +90 -0
  75. package/dist/server/better-auth-instance.js.map +1 -1
  76. package/dist/server/deep-link.d.ts +7 -0
  77. package/dist/server/deep-link.d.ts.map +1 -1
  78. package/dist/server/deep-link.js +13 -2
  79. package/dist/server/deep-link.js.map +1 -1
  80. package/dist/server/index.d.ts +1 -1
  81. package/dist/server/index.d.ts.map +1 -1
  82. package/dist/server/index.js +1 -1
  83. package/dist/server/index.js.map +1 -1
  84. package/dist/templates/default/.agents/skills/actions/SKILL.md +52 -1
  85. package/dist/templates/default/.agents/skills/security/SKILL.md +22 -0
  86. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +52 -1
  87. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +6 -4
  88. package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +11 -0
  89. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +22 -0
  90. package/docs/content/actions.md +50 -0
  91. package/docs/content/durable-resume.md +49 -0
  92. package/docs/content/external-agents.md +2 -2
  93. package/docs/content/human-approval.md +101 -0
  94. package/docs/content/observability.md +21 -0
  95. package/docs/content/observational-memory.md +63 -0
  96. package/docs/content/plan-plugin.md +5 -0
  97. package/docs/content/pr-visual-recap.md +4 -3
  98. package/docs/content/processors.md +99 -0
  99. package/docs/content/template-plan.md +78 -14
  100. package/package.json +6 -1
  101. package/src/templates/default/.agents/skills/actions/SKILL.md +52 -1
  102. package/src/templates/default/.agents/skills/security/SKILL.md +22 -0
  103. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +52 -1
  104. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +6 -4
  105. package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +11 -0
  106. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +22 -0
@@ -142,5 +142,85 @@ export declare function createProviderCorpusJobAction(options: CreateProviderCor
142
142
  deleted?: undefined;
143
143
  jobId?: undefined;
144
144
  }>;
145
+ export declare function createProviderCorpusJobReadAction(options: Pick<CreateProviderCorpusJobActionOptions, "appId">): import("../action.js").ActionDefinition<{
146
+ operation?: "status" | "list" | "results" | undefined;
147
+ jobId?: string | undefined;
148
+ offset?: unknown;
149
+ limit?: unknown;
150
+ }, {
151
+ job: {
152
+ id: string;
153
+ name: string;
154
+ mode: string;
155
+ status: ProviderCorpusJobStatus;
156
+ provider: string;
157
+ createdAt: string;
158
+ updatedAt: string;
159
+ };
160
+ coverage: {
161
+ pagesProcessed: number;
162
+ batchesProcessed: number;
163
+ itemsProcessed: number;
164
+ matchedItems: number;
165
+ totalHits: number;
166
+ storedHits: number;
167
+ truncatedHits: boolean;
168
+ };
169
+ checkpoint: Record<string, unknown>;
170
+ error: string | null;
171
+ nextResumeAt: string | null;
172
+ } | {
173
+ jobs: {
174
+ job: {
175
+ id: string;
176
+ name: string;
177
+ mode: string;
178
+ status: ProviderCorpusJobStatus;
179
+ provider: string;
180
+ createdAt: string;
181
+ updatedAt: string;
182
+ };
183
+ coverage: {
184
+ pagesProcessed: number;
185
+ batchesProcessed: number;
186
+ itemsProcessed: number;
187
+ matchedItems: number;
188
+ totalHits: number;
189
+ storedHits: number;
190
+ truncatedHits: boolean;
191
+ };
192
+ checkpoint: Record<string, unknown>;
193
+ error: string | null;
194
+ nextResumeAt: string | null;
195
+ }[];
196
+ total: number;
197
+ } | {
198
+ hits: Record<string, unknown>[];
199
+ offset: number;
200
+ limit: number;
201
+ job: {
202
+ id: string;
203
+ name: string;
204
+ mode: string;
205
+ status: ProviderCorpusJobStatus;
206
+ provider: string;
207
+ createdAt: string;
208
+ updatedAt: string;
209
+ };
210
+ coverage: {
211
+ pagesProcessed: number;
212
+ batchesProcessed: number;
213
+ itemsProcessed: number;
214
+ matchedItems: number;
215
+ totalHits: number;
216
+ storedHits: number;
217
+ truncatedHits: boolean;
218
+ };
219
+ checkpoint: Record<string, unknown>;
220
+ error: string | null;
221
+ nextResumeAt: string | null;
222
+ jobs?: undefined;
223
+ total?: undefined;
224
+ }>;
145
225
  export {};
146
226
  //# sourceMappingURL=corpus-jobs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"corpus-jobs.d.ts","sourceRoot":"","sources":["../../src/provider-api/corpus-jobs.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAqB,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE5E,OAAO,EASL,KAAK,uBAAuB,EAC7B,MAAM,wBAAwB,CAAC;AAEhC,KAAK,qBAAqB,GAAG;IAC3B,cAAc,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAChE,CAAC;AAEF,MAAM,WAAW,oCAAoC;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,qBAAqB,CAAC;CACzC;AAiOD,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAa9C"}
1
+ {"version":3,"file":"corpus-jobs.d.ts","sourceRoot":"","sources":["../../src/provider-api/corpus-jobs.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAqB,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE5E,OAAO,EASL,KAAK,uBAAuB,EAC7B,MAAM,wBAAwB,CAAC;AAEhC,KAAK,qBAAqB,GAAG;IAC3B,cAAc,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAChE,CAAC;AAEF,MAAM,WAAW,oCAAoC;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,qBAAqB,CAAC;CACzC;AA0PD,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,oCAAoC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAa9C;AAED,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,IAAI,CAAC,oCAAoC,EAAE,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAY7D"}
@@ -178,6 +178,18 @@ const ProviderCorpusJobSchema = z.object({
178
178
  .optional()
179
179
  .describe("Results/list limit."),
180
180
  });
181
+ const ProviderCorpusJobReadSchema = z.object({
182
+ operation: z.enum(["list", "status", "results"]).default("list"),
183
+ jobId: z.string().optional().describe("Existing job id for status/results."),
184
+ offset: z.coerce.number().int().min(0).optional().describe("Results offset."),
185
+ limit: z.coerce
186
+ .number()
187
+ .int()
188
+ .min(1)
189
+ .max(1_000)
190
+ .optional()
191
+ .describe("Results/list limit."),
192
+ });
181
193
  const DEFAULT_PAGE_BUDGET = 25;
182
194
  const DEFAULT_BATCH_BUDGET = 25;
183
195
  const DEFAULT_RUNTIME_MS = 90_000;
@@ -196,6 +208,17 @@ export function createProviderCorpusJobAction(options) {
196
208
  run: async (args) => runProviderCorpusJobAction(args, options),
197
209
  });
198
210
  }
211
+ export function createProviderCorpusJobReadAction(options) {
212
+ return defineAction({
213
+ description: "Read provider corpus job status and stored results for the current user. " +
214
+ "This read-only companion is for UI polling and status surfaces; it cannot start or continue provider requests.",
215
+ schema: ProviderCorpusJobReadSchema,
216
+ http: { method: "GET" },
217
+ readOnly: true,
218
+ agentTool: false,
219
+ run: async (args) => runProviderCorpusJobReadAction(args, options),
220
+ });
221
+ }
199
222
  async function runProviderCorpusJobAction(args, options) {
200
223
  const ctx = getCredentialContext();
201
224
  if (!ctx)
@@ -256,6 +279,45 @@ async function runProviderCorpusJobAction(args, options) {
256
279
  }
257
280
  throw new Error(`Unsupported provider corpus operation ${args.operation}.`);
258
281
  }
282
+ async function runProviderCorpusJobReadAction(args, options) {
283
+ const ctx = getCredentialContext();
284
+ if (!ctx)
285
+ throw new Error("No authenticated context for provider corpus jobs.");
286
+ if (args.operation === "list") {
287
+ const jobs = await listProviderCorpusJobs({
288
+ appId: options.appId,
289
+ ownerEmail: ctx.userEmail,
290
+ limit: args.limit,
291
+ });
292
+ return { jobs: jobs.map(jobStatus), total: jobs.length };
293
+ }
294
+ const jobId = args.jobId?.trim();
295
+ if (!jobId)
296
+ throw new Error(`${args.operation} requires jobId.`);
297
+ const job = await getProviderCorpusJob({
298
+ id: jobId,
299
+ appId: options.appId,
300
+ ownerEmail: ctx.userEmail,
301
+ });
302
+ if (!job) {
303
+ throw new Error(`Provider corpus job ${jobId} not found or belongs to another owner/app.`);
304
+ }
305
+ if (args.operation === "status")
306
+ return jobStatus(job);
307
+ const hits = await getProviderCorpusJobHits({
308
+ jobId,
309
+ appId: options.appId,
310
+ ownerEmail: ctx.userEmail,
311
+ offset: args.offset,
312
+ limit: args.limit,
313
+ });
314
+ return {
315
+ ...jobStatus(job),
316
+ hits,
317
+ offset: args.offset ?? 0,
318
+ limit: args.limit ?? 100,
319
+ };
320
+ }
259
321
  async function startJob(args, appId, ownerEmail) {
260
322
  if (!args.mode)
261
323
  throw new Error("start requires mode.");
@@ -697,7 +759,11 @@ function searchItems(items, search, options) {
697
759
  kind: match.kind,
698
760
  query: match.query,
699
761
  match: match.match,
700
- snippet: makeSnippet(field.text, match.index, search.contextChars),
762
+ snippet: match.snippet ??
763
+ makeSnippet(field.text, match.index, search.contextChars, match.endIndex),
764
+ ...(match.matchedTerms?.length
765
+ ? { matchedTerms: match.matchedTerms }
766
+ : {}),
701
767
  ...(Object.keys(metadata).length ? { metadata } : {}),
702
768
  });
703
769
  }
@@ -1072,21 +1138,54 @@ function findItemWideTermMatch(fields, search) {
1072
1138
  if (!terms.length || search.matchMode === "anyTerm")
1073
1139
  return null;
1074
1140
  const caseSensitive = Boolean(search.caseSensitive);
1141
+ const fieldWindows = fields
1142
+ .map((field) => {
1143
+ const hits = bestTermClusterInText(field.text, terms, caseSensitive).map((hit) => ({ ...hit, field }));
1144
+ if (hits.length !== terms.length)
1145
+ return null;
1146
+ const start = Math.min(...hits.map((hit) => hit.index));
1147
+ const end = Math.max(...hits.map((hit) => hit.end));
1148
+ return {
1149
+ field,
1150
+ hits,
1151
+ start,
1152
+ end,
1153
+ length: end - start,
1154
+ };
1155
+ })
1156
+ .filter((result) => Boolean(result))
1157
+ .sort((a, b) => a.length - b.length || a.start - b.start);
1158
+ const bestFieldWindow = fieldWindows[0];
1159
+ if (bestFieldWindow) {
1160
+ const context = boundedNumber(search.contextChars, 180, 20, 1_000);
1161
+ const snippet = bestFieldWindow.length > context * 4
1162
+ ? makeCombinedTermSnippet(bestFieldWindow.hits, search.contextChars)
1163
+ : undefined;
1164
+ return {
1165
+ field: bestFieldWindow.field,
1166
+ match: {
1167
+ kind: "allTerms",
1168
+ query: terms.join(" "),
1169
+ index: bestFieldWindow.start,
1170
+ endIndex: bestFieldWindow.end,
1171
+ match: bestFieldWindow.field.text.slice(bestFieldWindow.start, bestFieldWindow.end),
1172
+ matchedTerms: terms,
1173
+ snippet,
1174
+ },
1175
+ };
1176
+ }
1075
1177
  const termHits = terms.map((term) => {
1076
- const needle = caseSensitive ? term : term.toLowerCase();
1077
1178
  for (const field of fields) {
1078
- const haystack = caseSensitive ? field.text : field.text.toLowerCase();
1079
- const index = haystack.indexOf(needle);
1080
- if (index >= 0)
1081
- return { term, field, index };
1179
+ const hit = firstTermHitInField(field, term, caseSensitive);
1180
+ if (hit)
1181
+ return hit;
1082
1182
  }
1083
- return { term, field: null, index: -1 };
1183
+ return null;
1084
1184
  });
1085
- if (termHits.some((hit) => !hit.field || hit.index < 0))
1185
+ if (termHits.some((hit) => !hit))
1086
1186
  return null;
1087
- const first = termHits
1088
- .filter((hit) => Boolean(hit.field))
1089
- .sort((a, b) => a.index - b.index)[0];
1187
+ const hits = termHits.filter(isTermHit);
1188
+ const first = hits.sort((a, b) => a.index - b.index)[0];
1090
1189
  if (!first)
1091
1190
  return null;
1092
1191
  return {
@@ -1095,7 +1194,10 @@ function findItemWideTermMatch(fields, search) {
1095
1194
  kind: "allTerms",
1096
1195
  query: terms.join(" "),
1097
1196
  index: first.index,
1098
- match: first.term,
1197
+ endIndex: first.end,
1198
+ match: first.match,
1199
+ matchedTerms: terms,
1200
+ snippet: makeCombinedTermSnippet(hits, search.contextChars),
1099
1201
  },
1100
1202
  };
1101
1203
  }
@@ -1150,22 +1252,29 @@ function findSearchMatches(text, search, includeTerms) {
1150
1252
  addSubstring(query, query, "query");
1151
1253
  const terms = includeTerms ? normalizedTerms(search) : [];
1152
1254
  if (terms.length) {
1153
- const hits = terms
1154
- .map((term) => {
1155
- const needle = caseSensitive ? term : term.toLowerCase();
1156
- return { term, index: haystack.indexOf(needle) };
1157
- })
1158
- .filter((hit) => hit.index >= 0);
1159
1255
  const mode = search.matchMode === "anyTerm" ? "anyTerm" : "allTerms";
1256
+ const hits = mode === "allTerms"
1257
+ ? bestTermClusterInText(source, terms, caseSensitive)
1258
+ : terms
1259
+ .map((term) => firstTermHitInText(source, haystack, caseSensitive ? term : term.toLowerCase(), term))
1260
+ .filter(isInlineTermHit)
1261
+ .sort((a, b) => a.index - b.index);
1160
1262
  if ((mode === "allTerms" && hits.length === terms.length) ||
1161
1263
  (mode === "anyTerm" && hits.length > 0)) {
1162
- const first = hits.sort((a, b) => a.index - b.index)[0];
1264
+ const first = hits[0];
1163
1265
  if (first) {
1266
+ const endIndex = mode === "allTerms"
1267
+ ? Math.max(...hits.map((hit) => hit.end))
1268
+ : first.end;
1164
1269
  matches.push({
1165
1270
  kind: mode,
1166
1271
  query: terms.join(" "),
1167
1272
  index: first.index,
1168
- match: first.term,
1273
+ endIndex,
1274
+ match: mode === "allTerms"
1275
+ ? source.slice(first.index, endIndex)
1276
+ : first.match,
1277
+ matchedTerms: mode === "allTerms" ? terms : hits.map((hit) => hit.term),
1169
1278
  });
1170
1279
  }
1171
1280
  }
@@ -1179,14 +1288,102 @@ function normalizeRegexFlags(flags, caseSensitive) {
1179
1288
  const withCase = caseSensitive || /i/.test(allowed) ? allowed : `${allowed}i`;
1180
1289
  return `${withCase}g`;
1181
1290
  }
1182
- function makeSnippet(text, index, contextChars) {
1291
+ function makeSnippet(text, index, contextChars, endIndex = index) {
1183
1292
  const context = boundedNumber(contextChars, 180, 20, 1_000);
1184
1293
  const start = Math.max(0, index - context);
1185
- const end = Math.min(text.length, index + context);
1294
+ const end = Math.min(text.length, Math.max(index, endIndex) + context);
1186
1295
  const prefix = start > 0 ? "..." : "";
1187
1296
  const suffix = end < text.length ? "..." : "";
1188
1297
  return `${prefix}${text.slice(start, end)}${suffix}`
1189
1298
  .replace(/\s+/g, " ")
1190
1299
  .trim();
1191
1300
  }
1301
+ function firstTermHitInText(source, haystack, needle, term) {
1302
+ const index = haystack.indexOf(needle);
1303
+ return index >= 0
1304
+ ? {
1305
+ term,
1306
+ index,
1307
+ end: index + term.length,
1308
+ match: source.slice(index, index + term.length),
1309
+ }
1310
+ : null;
1311
+ }
1312
+ function firstTermHitInField(field, term, caseSensitive) {
1313
+ const source = field.text;
1314
+ const haystack = caseSensitive ? source : source.toLowerCase();
1315
+ const needle = caseSensitive ? term : term.toLowerCase();
1316
+ const hit = firstTermHitInText(source, haystack, needle, term);
1317
+ return hit ? { ...hit, term, field } : null;
1318
+ }
1319
+ function isInlineTermHit(hit) {
1320
+ return Boolean(hit);
1321
+ }
1322
+ function isTermHit(hit) {
1323
+ return Boolean(hit);
1324
+ }
1325
+ function bestTermClusterInText(source, terms, caseSensitive) {
1326
+ const haystack = caseSensitive ? source : source.toLowerCase();
1327
+ const allHits = terms.map((term) => {
1328
+ const needle = caseSensitive ? term : term.toLowerCase();
1329
+ const hits = [];
1330
+ let from = 0;
1331
+ while (from <= haystack.length) {
1332
+ const index = haystack.indexOf(needle, from);
1333
+ if (index < 0)
1334
+ break;
1335
+ hits.push({
1336
+ term,
1337
+ index,
1338
+ end: index + term.length,
1339
+ match: source.slice(index, index + term.length),
1340
+ });
1341
+ from = index + Math.max(1, needle.length);
1342
+ if (hits.length >= 100)
1343
+ break;
1344
+ }
1345
+ return hits;
1346
+ });
1347
+ if (allHits.some((hits) => hits.length === 0))
1348
+ return [];
1349
+ const flattened = allHits
1350
+ .flat()
1351
+ .sort((a, b) => a.index - b.index || a.end - b.end);
1352
+ let best = null;
1353
+ let bestLength = Infinity;
1354
+ for (let start = 0; start < flattened.length; start++) {
1355
+ const selected = new Map();
1356
+ for (let end = start; end < flattened.length; end++) {
1357
+ const hit = flattened[end];
1358
+ selected.set(hit.term, hit);
1359
+ if (selected.size !== terms.length)
1360
+ continue;
1361
+ const cluster = terms
1362
+ .map((term) => selected.get(term))
1363
+ .filter(isInlineTermHit)
1364
+ .sort((a, b) => a.index - b.index);
1365
+ const length = Math.max(...cluster.map((item) => item.end)) -
1366
+ Math.min(...cluster.map((item) => item.index));
1367
+ if (length < bestLength) {
1368
+ best = cluster;
1369
+ bestLength = length;
1370
+ }
1371
+ break;
1372
+ }
1373
+ }
1374
+ return best ?? [];
1375
+ }
1376
+ function makeCombinedTermSnippet(hits, contextChars) {
1377
+ const context = boundedNumber(contextChars, 180, 20, 1_000);
1378
+ const shownHits = hits.slice(0, 5);
1379
+ const perHitContext = Math.max(20, Math.min(120, Math.floor(context / Math.max(1, Math.min(3, hits.length)))));
1380
+ const parts = shownHits.map((hit) => {
1381
+ const snippet = makeSnippet(hit.field.text, hit.index, perHitContext, hit.end);
1382
+ return `${hit.field.path}: ${snippet}`;
1383
+ });
1384
+ if (hits.length > shownHits.length) {
1385
+ parts.push(`... +${hits.length - shownHits.length} more terms`);
1386
+ }
1387
+ return parts.join(" | ");
1388
+ }
1192
1389
  //# sourceMappingURL=corpus-jobs.js.map