pi-continuous-learning 0.5.0 → 0.6.0

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 (34) hide show
  1. package/dist/cli/analyze-single-shot.d.ts +56 -0
  2. package/dist/cli/analyze-single-shot.d.ts.map +1 -0
  3. package/dist/cli/analyze-single-shot.js +83 -0
  4. package/dist/cli/analyze-single-shot.js.map +1 -0
  5. package/dist/cli/analyze.js +70 -81
  6. package/dist/cli/analyze.js.map +1 -1
  7. package/dist/instinct-tools.d.ts +10 -0
  8. package/dist/instinct-tools.d.ts.map +1 -1
  9. package/dist/instinct-tools.js +38 -1
  10. package/dist/instinct-tools.js.map +1 -1
  11. package/dist/observation-preprocessor.d.ts +26 -0
  12. package/dist/observation-preprocessor.d.ts.map +1 -0
  13. package/dist/observation-preprocessor.js +31 -0
  14. package/dist/observation-preprocessor.js.map +1 -0
  15. package/dist/prompts/analyzer-system-single-shot.d.ts +6 -0
  16. package/dist/prompts/analyzer-system-single-shot.d.ts.map +1 -0
  17. package/dist/prompts/analyzer-system-single-shot.js +124 -0
  18. package/dist/prompts/analyzer-system-single-shot.js.map +1 -0
  19. package/dist/prompts/analyzer-user-single-shot.d.ts +22 -0
  20. package/dist/prompts/analyzer-user-single-shot.d.ts.map +1 -0
  21. package/dist/prompts/analyzer-user-single-shot.js +53 -0
  22. package/dist/prompts/analyzer-user-single-shot.js.map +1 -0
  23. package/dist/prompts/analyzer-user.d.ts +3 -1
  24. package/dist/prompts/analyzer-user.d.ts.map +1 -1
  25. package/dist/prompts/analyzer-user.js +20 -7
  26. package/dist/prompts/analyzer-user.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/cli/analyze-single-shot.ts +145 -0
  29. package/src/cli/analyze.ts +82 -124
  30. package/src/instinct-tools.ts +42 -1
  31. package/src/observation-preprocessor.ts +48 -0
  32. package/src/prompts/analyzer-system-single-shot.ts +123 -0
  33. package/src/prompts/analyzer-user-single-shot.ts +88 -0
  34. package/src/prompts/analyzer-user.ts +26 -8
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Preprocess a single observation.
3
+ * Returns null if the observation should be dropped entirely.
4
+ * Returns a new (immutable) observation with large fields stripped if applicable.
5
+ */
6
+ export function preprocessObservation(obs) {
7
+ if (obs.event === "turn_start" || obs.event === "tool_start") {
8
+ return null;
9
+ }
10
+ if (obs.event === "tool_complete" && !obs.is_error) {
11
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
12
+ const { output: _, ...stripped } = obs;
13
+ return stripped;
14
+ }
15
+ return obs;
16
+ }
17
+ /**
18
+ * Preprocess an array of raw observations.
19
+ * Drops nulls and returns only the meaningful events.
20
+ */
21
+ export function preprocessObservations(observations) {
22
+ const result = [];
23
+ for (const obs of observations) {
24
+ const processed = preprocessObservation(obs);
25
+ if (processed !== null) {
26
+ result.push(processed);
27
+ }
28
+ }
29
+ return result;
30
+ }
31
+ //# sourceMappingURL=observation-preprocessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observation-preprocessor.js","sourceRoot":"","sources":["../src/observation-preprocessor.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAgB;IACpD,IAAI,GAAG,CAAC,KAAK,KAAK,YAAY,IAAI,GAAG,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,GAAG,CAAC,KAAK,KAAK,eAAe,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QACnD,6DAA6D;QAC7D,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,GAAG,CAAC;QACvC,OAAO,QAAuB,CAAC;IACjC,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,YAA2B;IAChE,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,MAAM,SAAS,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * System prompt for the single-shot (non-agentic) background analyzer.
3
+ * Instructs the model to return a JSON change-set instead of using tool calls.
4
+ */
5
+ export declare function buildSingleShotSystemPrompt(): string;
6
+ //# sourceMappingURL=analyzer-system-single-shot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer-system-single-shot.d.ts","sourceRoot":"","sources":["../../src/prompts/analyzer-system-single-shot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAsHpD"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * System prompt for the single-shot (non-agentic) background analyzer.
3
+ * Instructs the model to return a JSON change-set instead of using tool calls.
4
+ */
5
+ export function buildSingleShotSystemPrompt() {
6
+ return `You are a coding behavior analyst. Your job is to read session observations
7
+ and produce a JSON change-set to create or update instinct files that capture reusable coding patterns.
8
+
9
+ ## Output Format
10
+
11
+ Return ONLY a valid JSON object (no prose, no markdown fences) with this structure:
12
+
13
+ {
14
+ "changes": [
15
+ {
16
+ "action": "create",
17
+ "instinct": {
18
+ "id": "kebab-case-id",
19
+ "title": "Short title",
20
+ "trigger": "When this should activate",
21
+ "action": "What the agent should do (verb phrase)",
22
+ "confidence": 0.5,
23
+ "domain": "typescript",
24
+ "scope": "project",
25
+ "observation_count": 3,
26
+ "confirmed_count": 0,
27
+ "contradicted_count": 0,
28
+ "inactive_count": 0,
29
+ "evidence": ["brief note 1", "brief note 2"]
30
+ }
31
+ },
32
+ {
33
+ "action": "update",
34
+ "instinct": { "...same fields as create..." }
35
+ },
36
+ {
37
+ "action": "delete",
38
+ "id": "instinct-id-to-delete",
39
+ "scope": "project"
40
+ }
41
+ ]
42
+ }
43
+
44
+ Return { "changes": [] } if no changes are needed.
45
+
46
+ ## Pattern Detection Heuristics
47
+
48
+ Analyze observations for these categories:
49
+
50
+ ### User Corrections
51
+ - User rephrases a request after an agent response
52
+ - User explicitly rejects an approach
53
+ - Trigger: the corrected behavior; Action: the preferred approach
54
+
55
+ ### Error Resolutions
56
+ - Tool call returns is_error: true followed by a successful retry
57
+ - Trigger: the error condition; Action: the proven resolution
58
+
59
+ ### Repeated Workflows
60
+ - Same sequence of tool calls appears 3+ times
61
+ - Trigger: the workflow start condition; Action: the efficient path
62
+
63
+ ### Tool Preferences
64
+ - Agent consistently uses one tool over alternatives
65
+ - Trigger: the task type; Action: the preferred tool and parameters
66
+
67
+ ### Anti-Patterns
68
+ - Actions that consistently lead to errors or user corrections
69
+ - Trigger: the bad pattern situation; Action: what to do instead
70
+
71
+ ### Turn Structure
72
+ - turn_end events summarize turns: tool_count and error_count
73
+ - High error_count turns suggest inefficient approaches
74
+
75
+ ### Context Pressure
76
+ - session_compact events signal context window pressure
77
+
78
+ ### User Shell Commands
79
+ - user_bash events capture manual shell commands the user runs
80
+ - Repeated commands after agent actions reveal verification patterns
81
+
82
+ ### Model Preferences
83
+ - model_select events track when users switch models
84
+
85
+ ## Feedback Analysis
86
+
87
+ Each observation may include an active_instincts field listing instinct IDs
88
+ that were injected into the agent's system prompt before that turn.
89
+
90
+ Use this to update existing instinct confidence scores:
91
+ - Confirmed (+0.05): instinct was active and agent followed guidance without correction
92
+ - Contradicted (-0.15): instinct was active but user corrected the agent
93
+ - Inactive (no change): instinct was injected but trigger never arose
94
+
95
+ When updating, increment the corresponding count field and recalculate confidence.
96
+
97
+ ## Confidence Scoring Rules
98
+
99
+ ### Initial Confidence (new instincts)
100
+ - 1-2 observations -> 0.3
101
+ - 3-5 observations -> 0.5
102
+ - 6-10 observations -> 0.7
103
+ - 11+ observations -> 0.85
104
+
105
+ ### Clamping
106
+ - Always clamp to [0.1, 0.9]
107
+
108
+ ## Scope Decision Guide
109
+
110
+ Use project scope when the pattern is specific to this project's tech stack or conventions.
111
+ Use global scope when the pattern applies universally to any coding session.
112
+ When in doubt, prefer project scope.
113
+
114
+ ## Conservativeness Rules
115
+
116
+ 1. Only create a new instinct with 3+ clear independent observations supporting the pattern.
117
+ 2. No code snippets in the action field - plain language only.
118
+ 3. Each instinct must have one well-defined trigger.
119
+ 4. New instincts from observation data alone are capped at 0.85 confidence.
120
+ 5. Check existing instincts (provided in the user message) for duplicates before creating. Update instead.
121
+ 6. Write actions as clear instructions starting with a verb.
122
+ 7. Be skeptical of outliers - patterns seen only in unusual circumstances should not become instincts.`;
123
+ }
124
+ //# sourceMappingURL=analyzer-system-single-shot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer-system-single-shot.js","sourceRoot":"","sources":["../../src/prompts/analyzer-system-single-shot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;uGAoH8F,CAAC;AACxG,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * User prompt builder for the single-shot background analyzer.
3
+ * Includes current instincts inline (no tool calls needed) and filtered observations.
4
+ */
5
+ import type { InstalledSkill, Instinct, ProjectEntry } from "../types.js";
6
+ export interface SingleShotPromptOptions {
7
+ agentsMdProject?: string | null;
8
+ agentsMdGlobal?: string | null;
9
+ installedSkills?: InstalledSkill[];
10
+ }
11
+ /**
12
+ * Builds the user prompt for the single-shot analyzer.
13
+ * Embeds all current instincts inline so the model has full context
14
+ * without making any tool calls.
15
+ *
16
+ * @param project - Project metadata
17
+ * @param existingInstincts - All current instincts (project + global)
18
+ * @param observationLines - Preprocessed observation lines (JSONL strings)
19
+ * @param options - Optional AGENTS.md content and installed skills
20
+ */
21
+ export declare function buildSingleShotUserPrompt(project: ProjectEntry, existingInstincts: Instinct[], observationLines: string[], options?: SingleShotPromptOptions): string;
22
+ //# sourceMappingURL=analyzer-user-single-shot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer-user-single-shot.d.ts","sourceRoot":"","sources":["../../src/prompts/analyzer-user-single-shot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG1E,MAAM,WAAW,uBAAuB;IACtC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;CACpC;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,YAAY,EACrB,iBAAiB,EAAE,QAAQ,EAAE,EAC7B,gBAAgB,EAAE,MAAM,EAAE,EAC1B,OAAO,GAAE,uBAA4B,GACpC,MAAM,CA2DR"}
@@ -0,0 +1,53 @@
1
+ import { formatInstinctsForPrompt } from "../cli/analyze-single-shot.js";
2
+ /**
3
+ * Builds the user prompt for the single-shot analyzer.
4
+ * Embeds all current instincts inline so the model has full context
5
+ * without making any tool calls.
6
+ *
7
+ * @param project - Project metadata
8
+ * @param existingInstincts - All current instincts (project + global)
9
+ * @param observationLines - Preprocessed observation lines (JSONL strings)
10
+ * @param options - Optional AGENTS.md content and installed skills
11
+ */
12
+ export function buildSingleShotUserPrompt(project, existingInstincts, observationLines, options = {}) {
13
+ const { agentsMdProject = null, agentsMdGlobal = null, installedSkills = [] } = options;
14
+ const observationBlock = observationLines.length > 0
15
+ ? observationLines.join("\n")
16
+ : "(no observations recorded yet)";
17
+ const instinctBlock = formatInstinctsForPrompt(existingInstincts);
18
+ const parts = [
19
+ "## Project Context",
20
+ "",
21
+ `project_id: ${project.id}`,
22
+ `project_name: ${project.name}`,
23
+ "",
24
+ "## Existing Instincts",
25
+ "",
26
+ instinctBlock,
27
+ "",
28
+ "## New Observations (preprocessed)",
29
+ "",
30
+ "```",
31
+ observationBlock,
32
+ "```",
33
+ ];
34
+ if (agentsMdProject != null || agentsMdGlobal != null) {
35
+ parts.push("", "## Existing Guidelines", "");
36
+ if (agentsMdProject != null) {
37
+ parts.push("### Project AGENTS.md", "", agentsMdProject, "");
38
+ }
39
+ if (agentsMdGlobal != null) {
40
+ parts.push("### Global AGENTS.md", "", agentsMdGlobal, "");
41
+ }
42
+ }
43
+ if (installedSkills.length > 0) {
44
+ parts.push("", "## Installed Skills", "");
45
+ for (const skill of installedSkills) {
46
+ parts.push(`- **${skill.name}**: ${skill.description}`);
47
+ }
48
+ parts.push("");
49
+ }
50
+ parts.push("", "## Instructions", "", "1. Review the existing instincts above.", "2. Analyze the new observations for patterns per the system prompt rules.", "3. Return a JSON change-set: create new instincts, update existing ones, or delete obsolete ones.", "4. Apply feedback analysis using the active_instincts field in each observation.", "5. Passive confidence decay has already been applied before this analysis.", "", "Return ONLY the JSON object. No prose, no markdown fences.");
51
+ return parts.join("\n");
52
+ }
53
+ //# sourceMappingURL=analyzer-user-single-shot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer-user-single-shot.js","sourceRoot":"","sources":["../../src/prompts/analyzer-user-single-shot.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAQzE;;;;;;;;;GASG;AACH,MAAM,UAAU,yBAAyB,CACvC,OAAqB,EACrB,iBAA6B,EAC7B,gBAA0B,EAC1B,UAAmC,EAAE;IAErC,MAAM,EAAE,eAAe,GAAG,IAAI,EAAE,cAAc,GAAG,IAAI,EAAE,eAAe,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IAExF,MAAM,gBAAgB,GACpB,gBAAgB,CAAC,MAAM,GAAG,CAAC;QACzB,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;QAC7B,CAAC,CAAC,gCAAgC,CAAC;IAEvC,MAAM,aAAa,GAAG,wBAAwB,CAAC,iBAAiB,CAAC,CAAC;IAElE,MAAM,KAAK,GAAa;QACtB,oBAAoB;QACpB,EAAE;QACF,eAAe,OAAO,CAAC,EAAE,EAAE;QAC3B,iBAAiB,OAAO,CAAC,IAAI,EAAE;QAC/B,EAAE;QACF,uBAAuB;QACvB,EAAE;QACF,aAAa;QACb,EAAE;QACF,oCAAoC;QACpC,EAAE;QACF,KAAK;QACL,gBAAgB;QAChB,KAAK;KACN,CAAC;IAEF,IAAI,eAAe,IAAI,IAAI,IAAI,cAAc,IAAI,IAAI,EAAE,CAAC;QACtD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,wBAAwB,EAAE,EAAE,CAAC,CAAC;QAC7C,IAAI,eAAe,IAAI,IAAI,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,cAAc,IAAI,IAAI,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,qBAAqB,EAAE,EAAE,CAAC,CAAC;QAC1C,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,IAAI,CACR,EAAE,EACF,iBAAiB,EACjB,EAAE,EACF,yCAAyC,EACzC,2EAA2E,EAC3E,mGAAmG,EACnG,kFAAkF,EAClF,4EAA4E,EAC5E,EAAE,EACF,4DAA4D,CAC7D,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -15,8 +15,10 @@ export declare function tailObservations(observationsPath: string, maxEntries?:
15
15
  export interface TailSinceResult {
16
16
  lines: string[];
17
17
  totalLineCount: number;
18
+ /** Number of raw new lines before preprocessing. */
19
+ rawLineCount: number;
18
20
  }
19
- export declare function tailObservationsSince(observationsPath: string, sinceLineCount: number, maxEntries?: number): TailSinceResult;
21
+ export declare function tailObservationsSince(observationsPath: string, sinceLineCount: number, maxEntries?: number, preprocess?: boolean): TailSinceResult;
20
22
  export interface AnalyzerUserPromptOptions {
21
23
  agentsMdProject?: string | null;
22
24
  agentsMdGlobal?: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"analyzer-user.d.ts","sourceRoot":"","sources":["../../src/prompts/analyzer-user.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhE;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,gBAAgB,EAAE,MAAM,EACxB,UAAU,SAAmB,GAC5B,MAAM,EAAE,CAUV;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,qBAAqB,CACnC,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,UAAU,SAAmB,GAC5B,eAAe,CAoBjB;AAED,MAAM,WAAW,yBAAyB;IACxC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,gBAAgB,EAAE,MAAM,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,YAAY,EACrB,OAAO,GAAE,yBAA8B,GACtC,MAAM,CAmER"}
1
+ {"version":3,"file":"analyzer-user.d.ts","sourceRoot":"","sources":["../../src/prompts/analyzer-user.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AAM7E;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,gBAAgB,EAAE,MAAM,EACxB,UAAU,SAAmB,GAC5B,MAAM,EAAE,CAUV;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,qBAAqB,CACnC,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,EACtB,UAAU,SAAmB,EAC7B,UAAU,UAAO,GAChB,eAAe,CAkCjB;AAED,MAAM,WAAW,yBAAyB;IACxC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,gBAAgB,EAAE,MAAM,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,YAAY,EACrB,OAAO,GAAE,yBAA8B,GACtC,MAAM,CAmER"}
@@ -4,6 +4,7 @@
4
4
  * to locate observations and instinct files for the current project.
5
5
  */
6
6
  import { existsSync, readFileSync } from "node:fs";
7
+ import { preprocessObservations } from "../observation-preprocessor.js";
7
8
  /** Maximum number of observation lines to include in analysis. */
8
9
  const MAX_TAIL_ENTRIES = 500;
9
10
  /**
@@ -24,9 +25,9 @@ export function tailObservations(observationsPath, maxEntries = MAX_TAIL_ENTRIES
24
25
  .filter((l) => l.length > 0);
25
26
  return lines.slice(-maxEntries);
26
27
  }
27
- export function tailObservationsSince(observationsPath, sinceLineCount, maxEntries = MAX_TAIL_ENTRIES) {
28
+ export function tailObservationsSince(observationsPath, sinceLineCount, maxEntries = MAX_TAIL_ENTRIES, preprocess = true) {
28
29
  if (!existsSync(observationsPath)) {
29
- return { lines: [], totalLineCount: 0 };
30
+ return { lines: [], totalLineCount: 0, rawLineCount: 0 };
30
31
  }
31
32
  const content = readFileSync(observationsPath, "utf-8");
32
33
  const allLines = content
@@ -36,11 +37,23 @@ export function tailObservationsSince(observationsPath, sinceLineCount, maxEntri
36
37
  const totalLineCount = allLines.length;
37
38
  // If file was archived/reset (fewer lines than cursor), treat as fresh
38
39
  const effectiveSince = totalLineCount < sinceLineCount ? 0 : sinceLineCount;
39
- const newLines = allLines.slice(effectiveSince);
40
- return {
41
- lines: newLines.slice(-maxEntries),
42
- totalLineCount,
43
- };
40
+ const newLines = allLines.slice(effectiveSince).slice(-maxEntries);
41
+ const rawLineCount = newLines.length;
42
+ if (!preprocess) {
43
+ return { lines: newLines, totalLineCount, rawLineCount };
44
+ }
45
+ const parsed = [];
46
+ for (const line of newLines) {
47
+ try {
48
+ parsed.push(JSON.parse(line));
49
+ }
50
+ catch {
51
+ // skip malformed lines
52
+ }
53
+ }
54
+ const filtered = preprocessObservations(parsed);
55
+ const lines = filtered.map((obs) => JSON.stringify(obs));
56
+ return { lines, totalLineCount, rawLineCount };
44
57
  }
45
58
  /**
46
59
  * Builds the user prompt for the background Haiku analyzer.
@@ -1 +1 @@
1
- {"version":3,"file":"analyzer-user.js","sourceRoot":"","sources":["../../src/prompts/analyzer-user.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analyzer-user.js","sourceRoot":"","sources":["../../src/prompts/analyzer-user.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-continuous-learning",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "A Pi extension that observes coding sessions and distills patterns into reusable instincts.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Single-shot (non-agentic) analyzer core.
3
+ *
4
+ * Replaces the multi-turn agentic session with a single complete() call.
5
+ * The model receives all current instincts inline and returns a JSON change-set.
6
+ * Changes are applied client-side, eliminating the ~16x cache-read multiplier.
7
+ */
8
+ import type { AssistantMessage, Context } from "@mariozechner/pi-ai";
9
+ import { complete } from "@mariozechner/pi-ai";
10
+ import type { Instinct } from "../types.js";
11
+ import { serializeInstinct } from "../instinct-parser.js";
12
+
13
+ export interface InstinctChangePayload {
14
+ id: string;
15
+ title: string;
16
+ trigger: string;
17
+ action: string;
18
+ confidence: number;
19
+ domain: string;
20
+ scope: "project" | "global";
21
+ observation_count?: number;
22
+ confirmed_count?: number;
23
+ contradicted_count?: number;
24
+ inactive_count?: number;
25
+ evidence?: string[];
26
+ }
27
+
28
+ export interface InstinctChange {
29
+ action: "create" | "update" | "delete";
30
+ instinct?: InstinctChangePayload;
31
+ /** For delete: the instinct ID to remove. */
32
+ id?: string;
33
+ /** For delete: the scope to target. */
34
+ scope?: "project" | "global";
35
+ }
36
+
37
+ export interface SingleShotResult {
38
+ changes: InstinctChange[];
39
+ message: AssistantMessage;
40
+ }
41
+
42
+ /**
43
+ * Parses the model's raw text response into an array of InstinctChange.
44
+ * Strips markdown code fences if present. Throws on invalid JSON or schema.
45
+ */
46
+ export function parseChanges(raw: string): InstinctChange[] {
47
+ const stripped = raw
48
+ .replace(/^```(?:json)?\s*/i, "")
49
+ .replace(/\s*```\s*$/, "")
50
+ .trim();
51
+
52
+ let parsed: unknown;
53
+ try {
54
+ parsed = JSON.parse(stripped);
55
+ } catch (e) {
56
+ throw new Error(
57
+ `Analyzer returned invalid JSON: ${String(e)}\nRaw: ${raw.slice(0, 200)}`
58
+ );
59
+ }
60
+
61
+ if (
62
+ typeof parsed !== "object" ||
63
+ parsed === null ||
64
+ !Array.isArray((parsed as { changes?: unknown }).changes)
65
+ ) {
66
+ throw new Error(
67
+ `Analyzer response missing 'changes' array. Got: ${JSON.stringify(parsed).slice(0, 200)}`
68
+ );
69
+ }
70
+
71
+ return (parsed as { changes: InstinctChange[] }).changes;
72
+ }
73
+
74
+ /**
75
+ * Builds a full Instinct from a create/update change.
76
+ * Returns null for delete changes or changes with missing instinct data.
77
+ */
78
+ export function buildInstinctFromChange(
79
+ change: InstinctChange,
80
+ existing: Instinct | null,
81
+ projectId: string
82
+ ): Instinct | null {
83
+ if (change.action === "delete" || !change.instinct) {
84
+ return null;
85
+ }
86
+
87
+ const now = new Date().toISOString();
88
+ const payload = change.instinct;
89
+
90
+ return {
91
+ id: payload.id,
92
+ title: payload.title,
93
+ trigger: payload.trigger,
94
+ action: payload.action,
95
+ confidence: Math.max(0.1, Math.min(0.9, payload.confidence)),
96
+ domain: payload.domain,
97
+ scope: payload.scope,
98
+ source: "personal",
99
+ ...(payload.scope === "project" ? { project_id: projectId } : {}),
100
+ created_at: existing?.created_at ?? now,
101
+ updated_at: now,
102
+ observation_count: payload.observation_count ?? 1,
103
+ confirmed_count: payload.confirmed_count ?? 0,
104
+ contradicted_count: payload.contradicted_count ?? 0,
105
+ inactive_count: payload.inactive_count ?? 0,
106
+ ...(payload.evidence !== undefined ? { evidence: payload.evidence } : {}),
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Formats existing instincts as serialized markdown blocks for inline context.
112
+ */
113
+ export function formatInstinctsForPrompt(instincts: Instinct[]): string {
114
+ if (instincts.length === 0) {
115
+ return "(no existing instincts)";
116
+ }
117
+ return instincts.map((i) => serializeInstinct(i)).join("\n---\n");
118
+ }
119
+
120
+ /**
121
+ * Runs a single complete() call with the provided context.
122
+ * Returns parsed changes and the raw AssistantMessage (for usage stats).
123
+ */
124
+ export async function runSingleShot(
125
+ context: Context,
126
+ model: Parameters<typeof complete>[0],
127
+ apiKey: string,
128
+ signal?: AbortSignal
129
+ ): Promise<SingleShotResult> {
130
+ const opts: Parameters<typeof complete>[2] = { apiKey };
131
+ if (signal !== undefined) opts.signal = signal;
132
+ const message = await complete(model, context, opts);
133
+
134
+ const textContent = message.content
135
+ .filter((c) => c.type === "text")
136
+ .map((c) => (c as { type: "text"; text: string }).text)
137
+ .join("");
138
+
139
+ if (!textContent.trim()) {
140
+ throw new Error("Analyzer returned empty response");
141
+ }
142
+
143
+ const changes = parseChanges(textContent);
144
+ return { changes, message };
145
+ }