bikky 0.3.3 → 0.3.6
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/README.md +26 -6
- package/dist/cli.js +26 -6
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +255 -3
- package/dist/config.js.map +1 -1
- package/dist/config.test.d.ts +3 -2
- package/dist/config.test.d.ts.map +1 -1
- package/dist/config.test.js +95 -6
- package/dist/config.test.js.map +1 -1
- package/dist/daemon/capture-policy.d.ts +4 -4
- package/dist/daemon/capture-policy.d.ts.map +1 -1
- package/dist/daemon/capture-policy.js +8 -17
- package/dist/daemon/capture-policy.js.map +1 -1
- package/dist/daemon/capture-policy.test.js +2 -2
- package/dist/daemon/capture-policy.test.js.map +1 -1
- package/dist/daemon/consolidation.d.ts +4 -1
- package/dist/daemon/consolidation.d.ts.map +1 -1
- package/dist/daemon/consolidation.js +18 -4
- package/dist/daemon/consolidation.js.map +1 -1
- package/dist/daemon/entity-typing.d.ts +42 -0
- package/dist/daemon/entity-typing.d.ts.map +1 -0
- package/dist/daemon/entity-typing.js +295 -0
- package/dist/daemon/entity-typing.js.map +1 -0
- package/dist/daemon/entity-typing.test.d.ts +2 -0
- package/dist/daemon/entity-typing.test.d.ts.map +1 -0
- package/dist/daemon/entity-typing.test.js +50 -0
- package/dist/daemon/entity-typing.test.js.map +1 -0
- package/dist/daemon/episode-summary.d.ts +4 -8
- package/dist/daemon/episode-summary.d.ts.map +1 -1
- package/dist/daemon/episode-summary.js +52 -18
- package/dist/daemon/episode-summary.js.map +1 -1
- package/dist/daemon/episode-summary.test.js +10 -7
- package/dist/daemon/episode-summary.test.js.map +1 -1
- package/dist/daemon/extraction-quality.test.d.ts +2 -0
- package/dist/daemon/extraction-quality.test.d.ts.map +1 -0
- package/dist/daemon/extraction-quality.test.js +283 -0
- package/dist/daemon/extraction-quality.test.js.map +1 -0
- package/dist/daemon/extraction-rules.d.ts +131 -0
- package/dist/daemon/extraction-rules.d.ts.map +1 -0
- package/dist/daemon/extraction-rules.js +321 -0
- package/dist/daemon/extraction-rules.js.map +1 -0
- package/dist/daemon/extraction-rules.test.d.ts +2 -0
- package/dist/daemon/extraction-rules.test.d.ts.map +1 -0
- package/dist/daemon/extraction-rules.test.js +183 -0
- package/dist/daemon/extraction-rules.test.js.map +1 -0
- package/dist/daemon/extraction.d.ts +19 -1
- package/dist/daemon/extraction.d.ts.map +1 -1
- package/dist/daemon/extraction.js +183 -26
- package/dist/daemon/extraction.js.map +1 -1
- package/dist/daemon/extraction.test.js +96 -2
- package/dist/daemon/extraction.test.js.map +1 -1
- package/dist/daemon/loop.d.ts.map +1 -1
- package/dist/daemon/loop.js +22 -0
- package/dist/daemon/loop.js.map +1 -1
- package/dist/daemon/loop.test.d.ts +2 -0
- package/dist/daemon/loop.test.d.ts.map +1 -0
- package/dist/daemon/loop.test.js +85 -0
- package/dist/daemon/loop.test.js.map +1 -0
- package/dist/daemon/maintenance-state.d.ts +36 -0
- package/dist/daemon/maintenance-state.d.ts.map +1 -0
- package/dist/daemon/maintenance-state.js +95 -0
- package/dist/daemon/maintenance-state.js.map +1 -0
- package/dist/daemon/maintenance-state.test.d.ts +2 -0
- package/dist/daemon/maintenance-state.test.d.ts.map +1 -0
- package/dist/daemon/maintenance-state.test.js +56 -0
- package/dist/daemon/maintenance-state.test.js.map +1 -0
- package/dist/daemon/qdrant.d.ts +37 -1
- package/dist/daemon/qdrant.d.ts.map +1 -1
- package/dist/daemon/qdrant.js +107 -12
- package/dist/daemon/qdrant.js.map +1 -1
- package/dist/daemon/qdrant.test.js +57 -1
- package/dist/daemon/qdrant.test.js.map +1 -1
- package/dist/daemon/relations-vocab.d.ts +44 -0
- package/dist/daemon/relations-vocab.d.ts.map +1 -0
- package/dist/daemon/relations-vocab.js +168 -0
- package/dist/daemon/relations-vocab.js.map +1 -0
- package/dist/daemon/relations-vocab.test.d.ts +2 -0
- package/dist/daemon/relations-vocab.test.d.ts.map +1 -0
- package/dist/daemon/relations-vocab.test.js +69 -0
- package/dist/daemon/relations-vocab.test.js.map +1 -0
- package/dist/daemon/relations.d.ts +49 -34
- package/dist/daemon/relations.d.ts.map +1 -1
- package/dist/daemon/relations.js +249 -153
- package/dist/daemon/relations.js.map +1 -1
- package/dist/daemon/relations.test.d.ts +2 -0
- package/dist/daemon/relations.test.d.ts.map +1 -0
- package/dist/daemon/relations.test.js +36 -0
- package/dist/daemon/relations.test.js.map +1 -0
- package/dist/daemon/session-index.d.ts +1 -8
- package/dist/daemon/session-index.d.ts.map +1 -1
- package/dist/daemon/session-index.js +8 -10
- package/dist/daemon/session-index.js.map +1 -1
- package/dist/daemon/session-index.test.js +15 -9
- package/dist/daemon/session-index.test.js.map +1 -1
- package/dist/daemon/session-summary.d.ts +1 -8
- package/dist/daemon/session-summary.d.ts.map +1 -1
- package/dist/daemon/session-summary.js +17 -12
- package/dist/daemon/session-summary.js.map +1 -1
- package/dist/daemon/session-summary.test.js +5 -3
- package/dist/daemon/session-summary.test.js.map +1 -1
- package/dist/daemon/watcher-health.d.ts +20 -0
- package/dist/daemon/watcher-health.d.ts.map +1 -0
- package/dist/daemon/watcher-health.js +78 -0
- package/dist/daemon/watcher-health.js.map +1 -0
- package/dist/daemon/watcher-health.test.d.ts +5 -0
- package/dist/daemon/watcher-health.test.d.ts.map +1 -0
- package/dist/daemon/watcher-health.test.js +96 -0
- package/dist/daemon/watcher-health.test.js.map +1 -0
- package/dist/daemon/watcher.test.d.ts +3 -2
- package/dist/daemon/watcher.test.d.ts.map +1 -1
- package/dist/daemon/watcher.test.js +9 -19
- package/dist/daemon/watcher.test.js.map +1 -1
- package/dist/daemon/workstream-resolver.d.ts +76 -0
- package/dist/daemon/workstream-resolver.d.ts.map +1 -0
- package/dist/daemon/workstream-resolver.js +180 -0
- package/dist/daemon/workstream-resolver.js.map +1 -0
- package/dist/daemon/workstream-resolver.test.d.ts +2 -0
- package/dist/daemon/workstream-resolver.test.d.ts.map +1 -0
- package/dist/daemon/workstream-resolver.test.js +128 -0
- package/dist/daemon/workstream-resolver.test.js.map +1 -0
- package/dist/daemon/workstream-summary.d.ts +1 -8
- package/dist/daemon/workstream-summary.d.ts.map +1 -1
- package/dist/daemon/workstream-summary.js +22 -14
- package/dist/daemon/workstream-summary.js.map +1 -1
- package/dist/daemon/workstream-summary.test.js +9 -6
- package/dist/daemon/workstream-summary.test.js.map +1 -1
- package/dist/lib/qdrant-client.d.ts +34 -0
- package/dist/lib/qdrant-client.d.ts.map +1 -1
- package/dist/lib/qdrant-client.js +54 -0
- package/dist/lib/qdrant-client.js.map +1 -1
- package/dist/lib/qdrant-client.test.js +49 -1
- package/dist/lib/qdrant-client.test.js.map +1 -1
- package/dist/llm/inference/index.d.ts +2 -1
- package/dist/llm/inference/index.d.ts.map +1 -1
- package/dist/llm/inference/index.js +37 -2
- package/dist/llm/inference/index.js.map +1 -1
- package/dist/llm/inference/index.test.js +44 -3
- package/dist/llm/inference/index.test.js.map +1 -1
- package/dist/llm/inference/providers/bedrock.d.ts +23 -0
- package/dist/llm/inference/providers/bedrock.d.ts.map +1 -1
- package/dist/llm/inference/providers/bedrock.js +10 -1
- package/dist/llm/inference/providers/bedrock.js.map +1 -1
- package/dist/llm/inference/providers/bedrock.test.js +49 -2
- package/dist/llm/inference/providers/bedrock.test.js.map +1 -1
- package/dist/llm/inference/providers/ollama.d.ts.map +1 -1
- package/dist/llm/inference/providers/ollama.js +7 -1
- package/dist/llm/inference/providers/ollama.js.map +1 -1
- package/dist/llm/inference/providers/openai.d.ts.map +1 -1
- package/dist/llm/inference/providers/openai.js +7 -1
- package/dist/llm/inference/providers/openai.js.map +1 -1
- package/dist/llm/inference/providers/openai.test.js +38 -2
- package/dist/llm/inference/providers/openai.test.js.map +1 -1
- package/dist/llm/inference/providers/portkey.d.ts.map +1 -1
- package/dist/llm/inference/providers/portkey.js +7 -1
- package/dist/llm/inference/providers/portkey.js.map +1 -1
- package/dist/llm/inference/types.d.ts +15 -0
- package/dist/llm/inference/types.d.ts.map +1 -1
- package/dist/llm/telemetry.d.ts +8 -1
- package/dist/llm/telemetry.d.ts.map +1 -1
- package/dist/llm/telemetry.js.map +1 -1
- package/dist/mcp/helpers.d.ts.map +1 -1
- package/dist/mcp/helpers.js +17 -2
- package/dist/mcp/helpers.js.map +1 -1
- package/dist/mcp/taxonomy.d.ts +6 -18
- package/dist/mcp/taxonomy.d.ts.map +1 -1
- package/dist/mcp/taxonomy.js +15 -25
- package/dist/mcp/taxonomy.js.map +1 -1
- package/dist/mcp/taxonomy.test.js +23 -7
- package/dist/mcp/taxonomy.test.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +355 -17
- package/dist/mcp/tools.js.map +1 -1
- package/dist/mcp/tools.test.js +279 -5
- package/dist/mcp/tools.test.js.map +1 -1
- package/dist/privacy/redaction.d.ts +21 -0
- package/dist/privacy/redaction.d.ts.map +1 -0
- package/dist/privacy/redaction.js +83 -0
- package/dist/privacy/redaction.js.map +1 -0
- package/dist/privacy/redaction.test.d.ts +2 -0
- package/dist/privacy/redaction.test.d.ts.map +1 -0
- package/dist/privacy/redaction.test.js +51 -0
- package/dist/privacy/redaction.test.js.map +1 -0
- package/dist/prompts/distill.d.ts.map +1 -1
- package/dist/prompts/distill.js +3 -2
- package/dist/prompts/distill.js.map +1 -1
- package/dist/prompts/entity-typing.d.ts +18 -0
- package/dist/prompts/entity-typing.d.ts.map +1 -0
- package/dist/prompts/entity-typing.js +60 -0
- package/dist/prompts/entity-typing.js.map +1 -0
- package/dist/prompts/episode-summary.d.ts.map +1 -1
- package/dist/prompts/episode-summary.js +17 -3
- package/dist/prompts/episode-summary.js.map +1 -1
- package/dist/prompts/extraction.d.ts.map +1 -1
- package/dist/prompts/extraction.js +114 -5
- package/dist/prompts/extraction.js.map +1 -1
- package/dist/prompts/index.d.ts +1 -0
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +1 -0
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/relations.d.ts.map +1 -1
- package/dist/prompts/relations.js +72 -4
- package/dist/prompts/relations.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +7 -1
- package/dist/render.js.map +1 -1
- package/dist/render.test.js +3 -2
- package/dist/render.test.js.map +1 -1
- package/dist/status.d.ts +94 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +378 -0
- package/dist/status.js.map +1 -0
- package/dist/status.test.d.ts +5 -0
- package/dist/status.test.d.ts.map +1 -0
- package/dist/status.test.js +203 -0
- package/dist/status.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,44 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for the session watcher.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Uses BIKKY_HOME to point bikky at an isolated tempdir for the entire test
|
|
5
|
+
* file — this prevents saveConfig() from clobbering the user's real
|
|
6
|
+
* ~/.bikky/config.json if a test crashes mid-run (root cause of issue #58).
|
|
6
7
|
*/
|
|
7
8
|
import { describe, it, before, after, beforeEach } from "node:test";
|
|
8
9
|
import assert from "node:assert/strict";
|
|
9
10
|
import fs from "node:fs";
|
|
10
11
|
import path from "node:path";
|
|
11
12
|
import os from "node:os";
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
// Set BIKKY_HOME *before* importing config so all derived paths use it.
|
|
14
|
+
const TEST_BIKKY_HOME = fs.mkdtempSync(path.join(os.tmpdir(), "bikky-home-watcher-"));
|
|
15
|
+
process.env.BIKKY_HOME = TEST_BIKKY_HOME;
|
|
16
|
+
const { resetConfig, loadConfig, saveConfig, CONFIG_DEFAULTS } = await import("../config.js");
|
|
17
|
+
const { discoverSessions } = await import("./watcher.js");
|
|
14
18
|
// ---------------------------------------------------------------------------
|
|
15
19
|
// Test directory setup
|
|
16
20
|
// ---------------------------------------------------------------------------
|
|
17
21
|
let testDir;
|
|
18
|
-
let savedConfig = null;
|
|
19
22
|
function createTestDir() {
|
|
20
23
|
return fs.mkdtempSync(path.join(os.tmpdir(), "bikky-test-sessions-"));
|
|
21
24
|
}
|
|
22
25
|
describe("discoverSessions", () => {
|
|
23
26
|
before(() => {
|
|
24
|
-
// Save existing config state
|
|
25
|
-
const configPath = path.join(os.homedir(), ".bikky", "config.json");
|
|
26
|
-
if (fs.existsSync(configPath)) {
|
|
27
|
-
savedConfig = fs.readFileSync(configPath, "utf-8");
|
|
28
|
-
}
|
|
29
27
|
testDir = createTestDir();
|
|
30
28
|
});
|
|
31
29
|
after(() => {
|
|
32
|
-
// Clean up test directory
|
|
33
30
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
34
|
-
|
|
35
|
-
const configPath = path.join(os.homedir(), ".bikky", "config.json");
|
|
36
|
-
if (savedConfig !== null) {
|
|
37
|
-
fs.writeFileSync(configPath, savedConfig);
|
|
38
|
-
}
|
|
39
|
-
else if (fs.existsSync(configPath)) {
|
|
40
|
-
fs.unlinkSync(configPath);
|
|
41
|
-
}
|
|
31
|
+
fs.rmSync(TEST_BIKKY_HOME, { recursive: true, force: true });
|
|
42
32
|
resetConfig();
|
|
43
33
|
});
|
|
44
34
|
beforeEach(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watcher.test.js","sourceRoot":"","sources":["../../src/daemon/watcher.test.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"watcher.test.js","sourceRoot":"","sources":["../../src/daemon/watcher.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workstream key resolver.
|
|
3
|
+
*
|
|
4
|
+
* The episode-summary LLM emits a free-form `workstream_key` string. Without
|
|
5
|
+
* normalisation, "fix wa-cron", "fix-wa-cron", and "Fix WA Cron" become three
|
|
6
|
+
* different workstreams. This module fixes two problems:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Deterministic extraction.** Many transcripts contain high-precision
|
|
9
|
+
* keys we shouldn't ask the LLM to find: GitHub issue/PR numbers, JIRA
|
|
10
|
+
* keys, branch names, task-folder slugs. We extract those first and let
|
|
11
|
+
* the LLM fall back only when they're absent.
|
|
12
|
+
*
|
|
13
|
+
* 2. **Canonicalization + alias absorption.** Whatever key we end up with
|
|
14
|
+
* (deterministic or LLM) is canonicalised (lowercase, ascii-fold,
|
|
15
|
+
* kebab-case) and matched against an alias registry so trivial rephrasings
|
|
16
|
+
* collapse onto the same canonical key.
|
|
17
|
+
*
|
|
18
|
+
* The registry is in-memory v1 — the daemon seeds it from a Qdrant scroll once
|
|
19
|
+
* per inference cycle and discards it. Persisting the alias graph back to
|
|
20
|
+
* Qdrant is a follow-up.
|
|
21
|
+
*/
|
|
22
|
+
export type WorkstreamSource = "deterministic" | "alias" | "llm-new" | "none";
|
|
23
|
+
export interface DeterministicKey {
|
|
24
|
+
key: string;
|
|
25
|
+
source: "issue" | "jira" | "branch" | "task-folder";
|
|
26
|
+
confidence: number;
|
|
27
|
+
}
|
|
28
|
+
export interface ResolvedWorkstream {
|
|
29
|
+
key: string | null;
|
|
30
|
+
source: WorkstreamSource;
|
|
31
|
+
reason: string;
|
|
32
|
+
}
|
|
33
|
+
export interface WorkstreamRegistry {
|
|
34
|
+
/** Canonical → set of aliases (including the canonical itself). */
|
|
35
|
+
canonicalToAliases: Map<string, Set<string>>;
|
|
36
|
+
/** Alias → canonical (reverse index). */
|
|
37
|
+
aliasToCanonical: Map<string, string>;
|
|
38
|
+
}
|
|
39
|
+
/** Empty in-memory registry. */
|
|
40
|
+
export declare const emptyRegistry: () => WorkstreamRegistry;
|
|
41
|
+
/**
|
|
42
|
+
* Canonicalise a raw key:
|
|
43
|
+
* - lowercase
|
|
44
|
+
* - strip leading '#'
|
|
45
|
+
* - ascii-fold (best-effort: replace non-[a-z0-9] runs with '-')
|
|
46
|
+
* - collapse repeated '-'
|
|
47
|
+
* - trim leading/trailing '-'
|
|
48
|
+
*
|
|
49
|
+
* Returns empty string for inputs that contain no usable characters.
|
|
50
|
+
*/
|
|
51
|
+
export declare const canonicalizeKey: (raw: string) => string;
|
|
52
|
+
/**
|
|
53
|
+
* Scan the transcript for high-precision durable keys. Returns the first match
|
|
54
|
+
* by pattern priority order; null if none found. We intentionally do not return
|
|
55
|
+
* all matches — picking *one* canonical key is the whole point.
|
|
56
|
+
*/
|
|
57
|
+
export declare const extractDeterministicKey: (transcript: string) => DeterministicKey | null;
|
|
58
|
+
/** Add a canonical key to the registry, optionally with extra aliases. */
|
|
59
|
+
export declare const registerCanonical: (registry: WorkstreamRegistry, canonical: string, aliases?: string[]) => void;
|
|
60
|
+
/**
|
|
61
|
+
* Resolve a workstream key for an episode.
|
|
62
|
+
*
|
|
63
|
+
* Decision order:
|
|
64
|
+
* 1. Deterministic extraction from the transcript wins outright.
|
|
65
|
+
* 2. Otherwise, canonicalise the LLM-proposed key. If it matches an existing
|
|
66
|
+
* alias in the registry, return the canonical for that alias.
|
|
67
|
+
* 3. Otherwise, accept the canonicalised LLM key as a brand-new canonical
|
|
68
|
+
* (caller is responsible for registering it if desired).
|
|
69
|
+
* 4. If neither source provides a usable key, return null.
|
|
70
|
+
*/
|
|
71
|
+
export declare const resolveWorkstreamKey: (input: {
|
|
72
|
+
transcript: string;
|
|
73
|
+
llmKey?: string | null;
|
|
74
|
+
registry?: WorkstreamRegistry;
|
|
75
|
+
}) => ResolvedWorkstream;
|
|
76
|
+
//# sourceMappingURL=workstream-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workstream-resolver.d.ts","sourceRoot":"","sources":["../../src/daemon/workstream-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAE9E,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,aAAa,CAAC;IACpD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7C,yCAAyC;IACzC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED,gCAAgC;AAChC,eAAO,MAAM,aAAa,QAAO,kBAG/B,CAAC;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,KAAG,MAU7C,CAAC;AA0DF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAAI,YAAY,MAAM,KAAG,gBAAgB,GAAG,IAa/E,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,GAC5B,UAAU,kBAAkB,EAC5B,WAAW,MAAM,EACjB,UAAS,MAAM,EAAO,KACrB,IAaF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CAC/B,KAAG,kBAsCH,CAAC"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workstream key resolver.
|
|
3
|
+
*
|
|
4
|
+
* The episode-summary LLM emits a free-form `workstream_key` string. Without
|
|
5
|
+
* normalisation, "fix wa-cron", "fix-wa-cron", and "Fix WA Cron" become three
|
|
6
|
+
* different workstreams. This module fixes two problems:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Deterministic extraction.** Many transcripts contain high-precision
|
|
9
|
+
* keys we shouldn't ask the LLM to find: GitHub issue/PR numbers, JIRA
|
|
10
|
+
* keys, branch names, task-folder slugs. We extract those first and let
|
|
11
|
+
* the LLM fall back only when they're absent.
|
|
12
|
+
*
|
|
13
|
+
* 2. **Canonicalization + alias absorption.** Whatever key we end up with
|
|
14
|
+
* (deterministic or LLM) is canonicalised (lowercase, ascii-fold,
|
|
15
|
+
* kebab-case) and matched against an alias registry so trivial rephrasings
|
|
16
|
+
* collapse onto the same canonical key.
|
|
17
|
+
*
|
|
18
|
+
* The registry is in-memory v1 — the daemon seeds it from a Qdrant scroll once
|
|
19
|
+
* per inference cycle and discards it. Persisting the alias graph back to
|
|
20
|
+
* Qdrant is a follow-up.
|
|
21
|
+
*/
|
|
22
|
+
/** Empty in-memory registry. */
|
|
23
|
+
export const emptyRegistry = () => ({
|
|
24
|
+
canonicalToAliases: new Map(),
|
|
25
|
+
aliasToCanonical: new Map(),
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Canonicalise a raw key:
|
|
29
|
+
* - lowercase
|
|
30
|
+
* - strip leading '#'
|
|
31
|
+
* - ascii-fold (best-effort: replace non-[a-z0-9] runs with '-')
|
|
32
|
+
* - collapse repeated '-'
|
|
33
|
+
* - trim leading/trailing '-'
|
|
34
|
+
*
|
|
35
|
+
* Returns empty string for inputs that contain no usable characters.
|
|
36
|
+
*/
|
|
37
|
+
export const canonicalizeKey = (raw) => {
|
|
38
|
+
if (typeof raw !== "string")
|
|
39
|
+
return "";
|
|
40
|
+
return raw
|
|
41
|
+
.normalize("NFKD")
|
|
42
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
43
|
+
.toLowerCase()
|
|
44
|
+
.replace(/^#+/, "")
|
|
45
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
46
|
+
.replace(/-+/g, "-")
|
|
47
|
+
.replace(/^-+|-+$/g, "");
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Tokens that look like JIRA keys but never are. Common protocol/encoding/
|
|
51
|
+
* convention names that follow the same `LETTERS-DIGITS` shape.
|
|
52
|
+
*/
|
|
53
|
+
const JIRA_DENYLIST = new Set([
|
|
54
|
+
"http", "https", "utf", "ascii", "iso", "rfc", "ietf", "ipv",
|
|
55
|
+
"gh", "pr", "tcp", "udp", "sha", "md", "h2", "h3", "ws",
|
|
56
|
+
"feat", "fix", "chore", "docs", "test", "perf", "ci", "build", "refactor",
|
|
57
|
+
]);
|
|
58
|
+
const DETERMINISTIC_PATTERNS = [
|
|
59
|
+
// Task folder slug — highest precision (we created it ourselves).
|
|
60
|
+
{
|
|
61
|
+
re: /\btasks\/(\d{3}-[a-z0-9][a-z0-9-]*)/i,
|
|
62
|
+
source: "task-folder",
|
|
63
|
+
confidence: 0.95,
|
|
64
|
+
format: (m) => m[1].toLowerCase(),
|
|
65
|
+
},
|
|
66
|
+
// GitHub issue/PR refs: #123, GH-123, gh-123. Checked BEFORE JIRA so
|
|
67
|
+
// `GH-44` doesn't get misclassified as a JIRA project key.
|
|
68
|
+
{
|
|
69
|
+
re: /(?:^|[^A-Za-z0-9_])(?:#|gh-|GH-)(\d{1,6})\b/,
|
|
70
|
+
source: "issue",
|
|
71
|
+
confidence: 0.9,
|
|
72
|
+
format: (m) => `gh-${m[1]}`,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
re: /\b(?:issue|pr|pull\s+request)\s*#?(\d{1,6})\b/i,
|
|
76
|
+
source: "issue",
|
|
77
|
+
confidence: 0.85,
|
|
78
|
+
format: (m) => `gh-${m[1]}`,
|
|
79
|
+
},
|
|
80
|
+
// JIRA-style key: 3-6 uppercase letters + dash + digits (≥2 to avoid
|
|
81
|
+
// matching things like `H2-1`). Reject known protocol/encoding tokens.
|
|
82
|
+
{
|
|
83
|
+
re: /\b([A-Z]{3,6})-(\d{2,})\b/,
|
|
84
|
+
source: "jira",
|
|
85
|
+
confidence: 0.95,
|
|
86
|
+
format: (m) => `${m[1].toLowerCase()}-${m[2]}`,
|
|
87
|
+
reject: (m) => JIRA_DENYLIST.has(m[1].toLowerCase()),
|
|
88
|
+
},
|
|
89
|
+
// Conventional branch names.
|
|
90
|
+
{
|
|
91
|
+
re: /\b(?:feat|fix|chore|refactor|docs|test|perf|ci|build)\/([a-z0-9][a-z0-9-]{2,40})\b/i,
|
|
92
|
+
source: "branch",
|
|
93
|
+
confidence: 0.85,
|
|
94
|
+
format: (m) => m[1].toLowerCase(),
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
/**
|
|
98
|
+
* Scan the transcript for high-precision durable keys. Returns the first match
|
|
99
|
+
* by pattern priority order; null if none found. We intentionally do not return
|
|
100
|
+
* all matches — picking *one* canonical key is the whole point.
|
|
101
|
+
*/
|
|
102
|
+
export const extractDeterministicKey = (transcript) => {
|
|
103
|
+
if (!transcript || typeof transcript !== "string")
|
|
104
|
+
return null;
|
|
105
|
+
for (const pattern of DETERMINISTIC_PATTERNS) {
|
|
106
|
+
const m = transcript.match(pattern.re);
|
|
107
|
+
if (m) {
|
|
108
|
+
const key = pattern.format(m);
|
|
109
|
+
if (pattern.reject?.(m))
|
|
110
|
+
continue;
|
|
111
|
+
if (key.length > 0) {
|
|
112
|
+
return { key, source: pattern.source, confidence: pattern.confidence };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
};
|
|
118
|
+
/** Add a canonical key to the registry, optionally with extra aliases. */
|
|
119
|
+
export const registerCanonical = (registry, canonical, aliases = []) => {
|
|
120
|
+
const c = canonicalizeKey(canonical);
|
|
121
|
+
if (!c)
|
|
122
|
+
return;
|
|
123
|
+
const set = registry.canonicalToAliases.get(c) ?? new Set();
|
|
124
|
+
set.add(c);
|
|
125
|
+
registry.aliasToCanonical.set(c, c);
|
|
126
|
+
for (const a of aliases) {
|
|
127
|
+
const ac = canonicalizeKey(a);
|
|
128
|
+
if (!ac)
|
|
129
|
+
continue;
|
|
130
|
+
set.add(ac);
|
|
131
|
+
registry.aliasToCanonical.set(ac, c);
|
|
132
|
+
}
|
|
133
|
+
registry.canonicalToAliases.set(c, set);
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Resolve a workstream key for an episode.
|
|
137
|
+
*
|
|
138
|
+
* Decision order:
|
|
139
|
+
* 1. Deterministic extraction from the transcript wins outright.
|
|
140
|
+
* 2. Otherwise, canonicalise the LLM-proposed key. If it matches an existing
|
|
141
|
+
* alias in the registry, return the canonical for that alias.
|
|
142
|
+
* 3. Otherwise, accept the canonicalised LLM key as a brand-new canonical
|
|
143
|
+
* (caller is responsible for registering it if desired).
|
|
144
|
+
* 4. If neither source provides a usable key, return null.
|
|
145
|
+
*/
|
|
146
|
+
export const resolveWorkstreamKey = (input) => {
|
|
147
|
+
const registry = input.registry ?? emptyRegistry();
|
|
148
|
+
const deterministic = extractDeterministicKey(input.transcript);
|
|
149
|
+
if (deterministic) {
|
|
150
|
+
const canonical = canonicalizeKey(deterministic.key);
|
|
151
|
+
const known = registry.aliasToCanonical.get(canonical);
|
|
152
|
+
return {
|
|
153
|
+
key: known ?? canonical,
|
|
154
|
+
source: known ? "alias" : "deterministic",
|
|
155
|
+
reason: `deterministic ${deterministic.source} match (${deterministic.key})`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const llm = (input.llmKey ?? "").trim();
|
|
159
|
+
if (!llm) {
|
|
160
|
+
return { key: null, source: "none", reason: "no deterministic key found and LLM returned no key" };
|
|
161
|
+
}
|
|
162
|
+
const canonical = canonicalizeKey(llm);
|
|
163
|
+
if (!canonical) {
|
|
164
|
+
return { key: null, source: "none", reason: `LLM key "${llm}" canonicalised to empty string` };
|
|
165
|
+
}
|
|
166
|
+
const known = registry.aliasToCanonical.get(canonical);
|
|
167
|
+
if (known) {
|
|
168
|
+
return {
|
|
169
|
+
key: known,
|
|
170
|
+
source: "alias",
|
|
171
|
+
reason: `LLM key "${llm}" matched existing canonical "${known}"`,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
key: canonical,
|
|
176
|
+
source: "llm-new",
|
|
177
|
+
reason: `LLM key "${llm}" registered as new canonical "${canonical}"`,
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
//# sourceMappingURL=workstream-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workstream-resolver.js","sourceRoot":"","sources":["../../src/daemon/workstream-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workstream-resolver.test.d.ts","sourceRoot":"","sources":["../../src/daemon/workstream-resolver.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { canonicalizeKey, emptyRegistry, extractDeterministicKey, registerCanonical, resolveWorkstreamKey, } from "./workstream-resolver.js";
|
|
4
|
+
test("canonicalizeKey: lowercase + kebab-case + ascii-fold", () => {
|
|
5
|
+
assert.equal(canonicalizeKey("Fix WA Cron"), "fix-wa-cron");
|
|
6
|
+
assert.equal(canonicalizeKey("fix_wa_cron"), "fix-wa-cron");
|
|
7
|
+
assert.equal(canonicalizeKey(" Fix WA Cron "), "fix-wa-cron");
|
|
8
|
+
assert.equal(canonicalizeKey("#GH-123"), "gh-123");
|
|
9
|
+
assert.equal(canonicalizeKey("café-déjà"), "cafe-deja");
|
|
10
|
+
assert.equal(canonicalizeKey(""), "");
|
|
11
|
+
assert.equal(canonicalizeKey("---"), "");
|
|
12
|
+
});
|
|
13
|
+
test("extractDeterministicKey: GitHub issue/PR refs", () => {
|
|
14
|
+
assert.deepEqual(extractDeterministicKey("see #123 for context"), {
|
|
15
|
+
key: "gh-123",
|
|
16
|
+
source: "issue",
|
|
17
|
+
confidence: 0.9,
|
|
18
|
+
});
|
|
19
|
+
assert.deepEqual(extractDeterministicKey("opened GH-44 yesterday"), {
|
|
20
|
+
key: "gh-44",
|
|
21
|
+
source: "issue",
|
|
22
|
+
confidence: 0.9,
|
|
23
|
+
});
|
|
24
|
+
assert.deepEqual(extractDeterministicKey("see issue 7"), {
|
|
25
|
+
key: "gh-7",
|
|
26
|
+
source: "issue",
|
|
27
|
+
confidence: 0.85,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
test("extractDeterministicKey: JIRA-style keys", () => {
|
|
31
|
+
assert.deepEqual(extractDeterministicKey("PROJ-456 was rejected"), {
|
|
32
|
+
key: "proj-456",
|
|
33
|
+
source: "jira",
|
|
34
|
+
confidence: 0.95,
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
test("extractDeterministicKey: branch names", () => {
|
|
38
|
+
assert.deepEqual(extractDeterministicKey("on feat/extraction-reliability today"), {
|
|
39
|
+
key: "extraction-reliability",
|
|
40
|
+
source: "branch",
|
|
41
|
+
confidence: 0.85,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
test("extractDeterministicKey: task-folder slugs", () => {
|
|
45
|
+
assert.deepEqual(extractDeterministicKey("editing tasks/252-bikky-extraction-reliability/plan.md"), {
|
|
46
|
+
key: "252-bikky-extraction-reliability",
|
|
47
|
+
source: "task-folder",
|
|
48
|
+
confidence: 0.95,
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
test("extractDeterministicKey: precedence (task-folder beats issue)", () => {
|
|
52
|
+
// task-folder pattern is first in the list, so it wins
|
|
53
|
+
assert.deepEqual(extractDeterministicKey("see #99 in tasks/100-foo-bar/plan.md"), { key: "100-foo-bar", source: "task-folder", confidence: 0.95 });
|
|
54
|
+
});
|
|
55
|
+
test("extractDeterministicKey: returns null when no patterns match", () => {
|
|
56
|
+
assert.equal(extractDeterministicKey("random chatter without any keys"), null);
|
|
57
|
+
assert.equal(extractDeterministicKey(""), null);
|
|
58
|
+
});
|
|
59
|
+
test("extractDeterministicKey: avoids false JIRA matches inside code-like tokens", () => {
|
|
60
|
+
// Hyphenated identifiers should not be misread as JIRA keys
|
|
61
|
+
assert.equal(extractDeterministicKey("see HTTP-200 status"), null);
|
|
62
|
+
assert.equal(extractDeterministicKey("UTF-8 encoding"), null);
|
|
63
|
+
});
|
|
64
|
+
test("resolveWorkstreamKey: deterministic wins over LLM key", () => {
|
|
65
|
+
const result = resolveWorkstreamKey({
|
|
66
|
+
transcript: "fixing #44 today",
|
|
67
|
+
llmKey: "some-other-key",
|
|
68
|
+
});
|
|
69
|
+
assert.equal(result.key, "gh-44");
|
|
70
|
+
assert.equal(result.source, "deterministic");
|
|
71
|
+
});
|
|
72
|
+
test("resolveWorkstreamKey: deterministic match against existing alias", () => {
|
|
73
|
+
const registry = emptyRegistry();
|
|
74
|
+
registerCanonical(registry, "issue-44", ["gh-44", "GH-44"]);
|
|
75
|
+
const result = resolveWorkstreamKey({
|
|
76
|
+
transcript: "working on #44",
|
|
77
|
+
registry,
|
|
78
|
+
});
|
|
79
|
+
assert.equal(result.key, "issue-44");
|
|
80
|
+
assert.equal(result.source, "alias");
|
|
81
|
+
});
|
|
82
|
+
test("resolveWorkstreamKey: LLM key absorbed by alias registry", () => {
|
|
83
|
+
const registry = emptyRegistry();
|
|
84
|
+
registerCanonical(registry, "fix-wa-cron", ["wa cron fix", "fix the wa cron"]);
|
|
85
|
+
const result = resolveWorkstreamKey({
|
|
86
|
+
transcript: "no anchors here",
|
|
87
|
+
llmKey: "Fix the WA Cron",
|
|
88
|
+
registry,
|
|
89
|
+
});
|
|
90
|
+
assert.equal(result.key, "fix-wa-cron");
|
|
91
|
+
assert.equal(result.source, "alias");
|
|
92
|
+
});
|
|
93
|
+
test("resolveWorkstreamKey: LLM key registered as new canonical", () => {
|
|
94
|
+
const result = resolveWorkstreamKey({
|
|
95
|
+
transcript: "no anchors here",
|
|
96
|
+
llmKey: "Brand New Project",
|
|
97
|
+
});
|
|
98
|
+
assert.equal(result.key, "brand-new-project");
|
|
99
|
+
assert.equal(result.source, "llm-new");
|
|
100
|
+
});
|
|
101
|
+
test("resolveWorkstreamKey: returns null when no key available", () => {
|
|
102
|
+
const result = resolveWorkstreamKey({
|
|
103
|
+
transcript: "no anchors",
|
|
104
|
+
llmKey: null,
|
|
105
|
+
});
|
|
106
|
+
assert.equal(result.key, null);
|
|
107
|
+
assert.equal(result.source, "none");
|
|
108
|
+
});
|
|
109
|
+
test("resolveWorkstreamKey: LLM key that canonicalises to empty returns null", () => {
|
|
110
|
+
const result = resolveWorkstreamKey({
|
|
111
|
+
transcript: "no anchors",
|
|
112
|
+
llmKey: "---",
|
|
113
|
+
});
|
|
114
|
+
assert.equal(result.key, null);
|
|
115
|
+
assert.equal(result.source, "none");
|
|
116
|
+
});
|
|
117
|
+
test("registerCanonical: aliases reverse-index correctly", () => {
|
|
118
|
+
const registry = emptyRegistry();
|
|
119
|
+
registerCanonical(registry, "main-task", ["alias-one", "Alias_Two"]);
|
|
120
|
+
assert.equal(registry.aliasToCanonical.get("main-task"), "main-task");
|
|
121
|
+
assert.equal(registry.aliasToCanonical.get("alias-one"), "main-task");
|
|
122
|
+
assert.equal(registry.aliasToCanonical.get("alias-two"), "main-task");
|
|
123
|
+
const aliases = registry.canonicalToAliases.get("main-task");
|
|
124
|
+
assert.ok(aliases?.has("main-task"));
|
|
125
|
+
assert.ok(aliases?.has("alias-one"));
|
|
126
|
+
assert.ok(aliases?.has("alias-two"));
|
|
127
|
+
});
|
|
128
|
+
//# sourceMappingURL=workstream-resolver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workstream-resolver.test.js","sourceRoot":"","sources":["../../src/daemon/workstream-resolver.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import type { BikkyConfig } from "../config.js";
|
|
2
2
|
import type { EpisodeSummaryWriteResult } from "./episode-summary.js";
|
|
3
3
|
import type { QdrantPayload } from "./qdrant.js";
|
|
4
|
+
import { type RedactionSummary } from "../privacy/redaction.js";
|
|
4
5
|
export { buildWorkstreamSummaryMessages } from "../prompts/index.js";
|
|
5
6
|
export interface WorkspaceScope {
|
|
6
7
|
workspaceId?: string;
|
|
7
8
|
actorId?: string;
|
|
8
9
|
includeLegacy?: boolean;
|
|
9
10
|
}
|
|
10
|
-
export interface RedactionSummary {
|
|
11
|
-
redacted: boolean;
|
|
12
|
-
summary: string;
|
|
13
|
-
matches: Array<{
|
|
14
|
-
type: string;
|
|
15
|
-
count: number;
|
|
16
|
-
}>;
|
|
17
|
-
}
|
|
18
11
|
export interface WorkstreamSummaryDraft {
|
|
19
12
|
content: string;
|
|
20
13
|
current_decisions: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workstream-summary.d.ts","sourceRoot":"","sources":["../../src/daemon/workstream-summary.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAShD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"workstream-summary.d.ts","sourceRoot":"","sources":["../../src/daemon/workstream-summary.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAShD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,8BAA8B,EAAE,MAAM,qBAAqB,CAAC;AAErE,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,SAAS,EAAE,gBAAgB,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAqBD,eAAO,MAAM,+BAA+B,GAC1C,gBAAgB,yBAAyB,EAAE,KAC1C,GAAG,CAAC,MAAM,EAAE,yBAAyB,EAAE,CAUzC,CAAC;AAEF,eAAO,MAAM,2BAA2B,GAAI,KAAK,MAAM,KAAG,sBAezD,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,eAAe,MAAM,EACrB,OAAO,cAAc,EACrB,OAAO,MAAM,GAAG,IAAI,KACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAoBxB,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,eAAe,MAAM,EACrB,OAAO,cAAc,KACpB,MAAM,CAAC,MAAM,EAAE,OAAO,CAiBxB,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,OAAO;IACnD,KAAK,EAAE,sBAAsB,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,cAAc,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;IACnE,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,gBAAgB,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;CAC5D,KAAG,8BA8DH,CAAC;AAyCF,eAAO,MAAM,yBAAyB,GAAU,OAAO;IACrD,cAAc,EAAE,yBAAyB,EAAE,CAAC;IAC5C,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;CACrB,KAAG,OAAO,CAAC,sBAAsB,EAAE,CA4CnC,CAAC"}
|
|
@@ -10,13 +10,8 @@ import { chatCompletion } from "../llm/index.js";
|
|
|
10
10
|
import { workstreamSummaryPrompt } from "../prompts/index.js";
|
|
11
11
|
import { CAPTURE_POLICY_VERSION, CAPTURE_TRIGGERS, DEFAULT_CAPTURE_CONTEXT, PROMPT_VERSIONS, } from "./capture-policy.js";
|
|
12
12
|
import * as qdrant from "./qdrant.js";
|
|
13
|
+
import { combineRedactions, redactStorageText, } from "../privacy/redaction.js";
|
|
13
14
|
export { buildWorkstreamSummaryMessages } from "../prompts/index.js";
|
|
14
|
-
const noRedaction = () => ({ redacted: false, summary: "none", matches: [] });
|
|
15
|
-
const passthroughText = (text, _options) => ({
|
|
16
|
-
text,
|
|
17
|
-
...noRedaction(),
|
|
18
|
-
});
|
|
19
|
-
const combinePassThrough = () => noRedaction();
|
|
20
15
|
const contentHash = (text) => createHash("sha256").update(`workstream:${text}`).digest("hex");
|
|
21
16
|
const stripJsonFence = (raw) => raw.trim().replace(/^```(?:json)?\s*\n?/, "").replace(/\n?```\s*$/, "").trim();
|
|
22
17
|
const normalizeStringArray = (value) => {
|
|
@@ -101,12 +96,18 @@ export const buildWorkstreamEpisodeFilter = (workstreamKey, scope) => {
|
|
|
101
96
|
return filter;
|
|
102
97
|
};
|
|
103
98
|
export const buildWorkstreamSummaryPayload = (input) => {
|
|
104
|
-
const redactedContent =
|
|
105
|
-
const redactedDecisions = input.draft.current_decisions.map((decision) =>
|
|
106
|
-
const redactedNextSteps = input.draft.next_steps.map((step) =>
|
|
107
|
-
const redactedBlockers = input.draft.blockers.map((blocker) =>
|
|
108
|
-
const redactedEntities = input.draft.entities.map((entity) =>
|
|
109
|
-
const redaction =
|
|
99
|
+
const redactedContent = redactStorageText(input.draft.content, input.redactionOptions);
|
|
100
|
+
const redactedDecisions = input.draft.current_decisions.map((decision) => redactStorageText(decision, input.redactionOptions));
|
|
101
|
+
const redactedNextSteps = input.draft.next_steps.map((step) => redactStorageText(step, input.redactionOptions));
|
|
102
|
+
const redactedBlockers = input.draft.blockers.map((blocker) => redactStorageText(blocker, input.redactionOptions));
|
|
103
|
+
const redactedEntities = input.draft.entities.map((entity) => redactStorageText(entity, input.redactionOptions));
|
|
104
|
+
const redaction = combineRedactions([
|
|
105
|
+
redactedContent,
|
|
106
|
+
...redactedDecisions,
|
|
107
|
+
...redactedNextSteps,
|
|
108
|
+
...redactedBlockers,
|
|
109
|
+
...redactedEntities,
|
|
110
|
+
]);
|
|
110
111
|
const existingPayload = input.existing?.payload ?? {};
|
|
111
112
|
const payload = {
|
|
112
113
|
...existingPayload,
|
|
@@ -150,6 +151,9 @@ export const buildWorkstreamSummaryPayload = (input) => {
|
|
|
150
151
|
if (redaction.redacted) {
|
|
151
152
|
payload["redaction"] = redaction;
|
|
152
153
|
}
|
|
154
|
+
else {
|
|
155
|
+
delete payload["redaction"];
|
|
156
|
+
}
|
|
153
157
|
return { payload, redaction };
|
|
154
158
|
};
|
|
155
159
|
const findExistingWorkstreamSummary = async (workstreamKey, scope, repo) => {
|
|
@@ -169,7 +173,11 @@ const loadEpisodeSummaries = async (workstreamKey, scope) => {
|
|
|
169
173
|
return result.result?.points ?? [];
|
|
170
174
|
};
|
|
171
175
|
const summarizeWorkstream = async (input) => {
|
|
172
|
-
const
|
|
176
|
+
const rendered = workstreamSummaryPrompt(input);
|
|
177
|
+
const result = await chatCompletion({
|
|
178
|
+
...rendered,
|
|
179
|
+
telemetry: { subsystem: "summary", workstream_key: input.workstreamKey, trigger: "workstream_summary" },
|
|
180
|
+
});
|
|
173
181
|
if (!result)
|
|
174
182
|
throw new Error("Workstream summary LLM returned null");
|
|
175
183
|
return parseWorkstreamSummaryDraft(result);
|
|
@@ -204,7 +212,7 @@ export const updateWorkstreamSummaries = async (input) => {
|
|
|
204
212
|
sourceEpisodeIds: episodes.map((episode) => episode.payload?.episode_id ?? episode.id).filter(Boolean),
|
|
205
213
|
repo: null,
|
|
206
214
|
redactionOptions: {
|
|
207
|
-
enabled:
|
|
215
|
+
enabled: true,
|
|
208
216
|
redactPii: false,
|
|
209
217
|
},
|
|
210
218
|
});
|