@mjasnikovs/pi-task 0.40.12 → 0.40.13

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.
@@ -44,9 +44,18 @@ export function isExcerptInContent(excerpt, content) {
44
44
  return false;
45
45
  return normaliseWhitespace(content).includes(ne);
46
46
  }
47
- /** The child writes these to mark its own join, so they are not source text and
48
- * not evidence of invention either. */
47
+ /**
48
+ * Tokens that are not source text and not evidence of invention either: the
49
+ * child's own elision marks, and the provenance tag the PROMPT gave it.
50
+ *
51
+ * `buildExtractionPrompt` wraps the identity as `<package>aeson@2.2.5.1</package>`,
52
+ * and re-run 5 caught a child quoting that back inside an otherwise verbatim
53
+ * excerpt — the only absent word in 24 unverified excerpts across three runs. A
54
+ * complete lowercase element is the whole rule; `Vec<String>` does not start with
55
+ * `<` and `<T>` has no closing tag, so neither is excused.
56
+ */
49
57
  const ELISION = /^(?:\.{3}|\u2026|\/\/\s*\.{3})$/;
58
+ const PROMPT_TAG = /^<([a-z_]+)>[^<]*<\/\1>$/;
50
59
  /**
51
60
  * Cover the excerpt with the longest runs the source actually contains, greedily.
52
61
  *
@@ -67,7 +76,7 @@ function coverBySource(normalisedExcerpt, normalisedContent) {
67
76
  run++;
68
77
  }
69
78
  if (run === 0) {
70
- if (!ELISION.test(words[i]))
79
+ if (!ELISION.test(words[i]) && !PROMPT_TAG.test(words[i]))
71
80
  absent.push(words[i]);
72
81
  i++;
73
82
  }
@@ -210,6 +210,16 @@ function definitionChunk(cache, opts, name) {
210
210
  * declaration per chunk, so the short chunk carrying the name IS its declaration and
211
211
  * the long ones are the prose that merely mentions it.
212
212
  */
213
+ /** The chunk without the `// path` / `-- path` line the indexer prepends. */
214
+ function chunkBody(content) {
215
+ return content.replace(/^(?:\/\/|--)[^\n]*\n/, '');
216
+ }
217
+ /**
218
+ * A chunk that only declares a MODULE. `pub mod serve;` is shorter than the
219
+ * function it contains and wins smallest-first, while being neither the value nor
220
+ * the type this hop exists to find.
221
+ */
222
+ const MODULE_DECL_RE = /^(?:#\[[^\n]*\]\s*)*(?:pub(?:\s*\([^)]*\))?\s+|export\s+|declare\s+)*(?:mod|module|namespace)\s+[A-Za-z_][\w']*\s*;?\s*$/;
213
223
  function valueChunk(cache, opts, name) {
214
224
  const rows = cache.db
215
225
  .prepare(`SELECT file_path, kind, content, 0 AS rank FROM chunks
@@ -219,7 +229,14 @@ function valueChunk(cache, opts, name) {
219
229
  // LIKE has no word boundary, so `decodeFile` matches `decodeFileStrict` — the
220
230
  // wrong declaration, and the exact confusion these runs keep producing.
221
231
  const whole = new RegExp(`(?<![A-Za-z0-9_])${escapeRe(name)}(?![A-Za-z0-9_])`);
222
- const row = rows.find(r => whole.test(r.content));
232
+ // Over the BODY, never the `// path` line the indexer prepends. Every chunk
233
+ // from `src/serve/mod.rs` carries "serve" in that line, so smallest-first
234
+ // handed axum's `serve` a 50-byte `pub struct TapIo` from a neighbouring file
235
+ // — the name was in the header and nowhere else.
236
+ const row = rows.find(r => {
237
+ const text = chunkBody(r.content);
238
+ return whole.test(text) && !MODULE_DECL_RE.test(text);
239
+ });
223
240
  if (!row)
224
241
  return null;
225
242
  return { filePath: row.file_path, kind: row.kind, content: row.content, rank: row.rank };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mjasnikovs/pi-task",
3
- "version": "0.40.12",
3
+ "version": "0.40.13",
4
4
  "description": "Deterministic task planning and spec-orchestration for local models — crash-safe /task pipelines with verify/enforce gates, a real-time remote web view, and web/docs/fetch/worker subagent tools.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",