principles-disciple 1.156.0 → 1.158.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.
@@ -0,0 +1,45 @@
1
+ /**
2
+ * PRI-482 Phase 3 — Production RuleContext v2 assembler
3
+ *
4
+ * Converts raw TrajectoryDatabase rows into a validated RuleHistoryWindow,
5
+ * and provides buildProductionRuleContext for gate integration.
6
+ *
7
+ * ERR prevention:
8
+ * - ERR-001: every row field is validated as unknown. No `as` bypass on
9
+ * parsed params_json or outcome.
10
+ * - ERR-024: buildProductionRuleContext fail-soft — query failure returns
11
+ * unavailable, never throws.
12
+ * - ERR-025: callers use recordToolCall → getRuleHostContextRows → this
13
+ * assembler, covering the full production chain.
14
+ *
15
+ * Spec: docs/superpowers/specs/2026-06-27-rulecode-context-vision-design.md §5.
16
+ */
17
+ import type { RuleContextV2, RuleHistoryWindow } from '@principles/core/runtime-v2';
18
+ import type { RuleHostContextResult, RuleHostContextRow } from './trajectory-types.js';
19
+ /**
20
+ * Minimal interface for the data source that TrajectoryDatabase satisfies.
21
+ * Allows testing buildProductionRuleContext without a real DB.
22
+ */
23
+ export interface RuleContextDataSource {
24
+ getRuleHostContextRows(sessionId: string, limit: number): RuleHostContextResult;
25
+ }
26
+ /**
27
+ * Convert raw DB rows into a validated RuleHistoryWindow.
28
+ *
29
+ * If ANY row is malformed (bad params_json, invalid outcome, empty tool_name),
30
+ * the entire window is marked unavailable (fail loud, spec §5.2).
31
+ *
32
+ * ERR-001: row fields are validated as unknown. params_json is JSON.parsed
33
+ * then structurally checked (must be a non-array object). No `as` bypass.
34
+ */
35
+ export declare function assembleHistoryFromRows(rows: readonly RuleHostContextRow[], truncated: boolean, projectDir: string): RuleHistoryWindow;
36
+ /**
37
+ * Build a RuleContextV2 from the production data source (TrajectoryDatabase).
38
+ *
39
+ * ERR-024: if the query or assembly fails, returns an unavailable context
40
+ * (fail-soft). Never throws.
41
+ *
42
+ * sameActionBlockCount is always null in this version (spec §5.4:
43
+ * session-tracker.blockedAttempts is a session total, not per-action).
44
+ */
45
+ export declare function buildProductionRuleContext(sessionId: string | null | undefined, targetPath: string | null, source: RuleContextDataSource, projectDir: string, limit?: number): RuleContextV2;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * PRI-482 Phase 3 — Production RuleContext v2 assembler
3
+ *
4
+ * Converts raw TrajectoryDatabase rows into a validated RuleHistoryWindow,
5
+ * and provides buildProductionRuleContext for gate integration.
6
+ *
7
+ * ERR prevention:
8
+ * - ERR-001: every row field is validated as unknown. No `as` bypass on
9
+ * parsed params_json or outcome.
10
+ * - ERR-024: buildProductionRuleContext fail-soft — query failure returns
11
+ * unavailable, never throws.
12
+ * - ERR-025: callers use recordToolCall → getRuleHostContextRows → this
13
+ * assembler, covering the full production chain.
14
+ *
15
+ * Spec: docs/superpowers/specs/2026-06-27-rulecode-context-vision-design.md §5.
16
+ */
17
+ import { canonicalizeToolKind, computeBehaviorFacts, extractFilePathFromParams, normalizePathPure, UNAVAILABLE_RULE_CONTEXT, } from '@principles/core/runtime-v2';
18
+ // ── internal constants ─────────────────────────────────────────────────────
19
+ const VALID_OUTCOMES = new Set([
20
+ 'success',
21
+ 'failure',
22
+ 'blocked',
23
+ ]);
24
+ const DEFAULT_HISTORY_LIMIT = 20;
25
+ // ── type guards (rc-2: no `as` bypass) ─────────────────────────────────────
26
+ function isPlainObject(value) {
27
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
28
+ }
29
+ function isRuleToolOutcome(value) {
30
+ return typeof value === 'string' && VALID_OUTCOMES.has(value);
31
+ }
32
+ // ── assembleHistoryFromRows ────────────────────────────────────────────────
33
+ /**
34
+ * Convert raw DB rows into a validated RuleHistoryWindow.
35
+ *
36
+ * If ANY row is malformed (bad params_json, invalid outcome, empty tool_name),
37
+ * the entire window is marked unavailable (fail loud, spec §5.2).
38
+ *
39
+ * ERR-001: row fields are validated as unknown. params_json is JSON.parsed
40
+ * then structurally checked (must be a non-array object). No `as` bypass.
41
+ */
42
+ export function assembleHistoryFromRows(rows, truncated, projectDir) {
43
+ const records = [];
44
+ for (const row of rows) {
45
+ // ── validate tool_name (ERR-001: unknown) ──
46
+ const toolName = row.toolName;
47
+ if (typeof toolName !== 'string' || toolName.length === 0) {
48
+ return unavailable(`row id=${row.id}: tool_name must be a non-empty string`);
49
+ }
50
+ // ── validate outcome (ERR-001: unknown, enum check) ──
51
+ const outcome = row.outcome;
52
+ if (!isRuleToolOutcome(outcome)) {
53
+ return unavailable(`row id=${row.id}: outcome "${row.outcome}" is not a valid RuleToolOutcome`);
54
+ }
55
+ // ── validate & parse params_json (ERR-001: unknown) ──
56
+ const paramsJsonStr = row.paramsJson;
57
+ if (typeof paramsJsonStr !== 'string') {
58
+ return unavailable(`row id=${row.id}: params_json must be a string`);
59
+ }
60
+ let parsedParams;
61
+ try {
62
+ parsedParams = JSON.parse(paramsJsonStr);
63
+ }
64
+ catch {
65
+ return unavailable(`row id=${row.id}: params_json is not valid JSON`);
66
+ }
67
+ // Must be a non-array object (spec §5.2)
68
+ if (!isPlainObject(parsedParams)) {
69
+ return unavailable(`row id=${row.id}: params_json must parse to a non-array object`);
70
+ }
71
+ // ── build RuleToolCallRecord ──
72
+ const canonicalKind = canonicalizeToolKind(toolName);
73
+ // Extract normalized path using the same pure logic as buildRuleHostAction
74
+ // (avoids production/replay drift, spec §4.4)
75
+ const rawPath = extractFilePathFromParams(parsedParams, {
76
+ isBashTool: canonicalKind === 'execute',
77
+ isWriteTool: canonicalKind === 'write',
78
+ toolName,
79
+ });
80
+ const normalizedPath = normalizePathPure(rawPath, projectDir);
81
+ records.push({
82
+ sequenceId: row.id,
83
+ toolName,
84
+ canonicalKind,
85
+ normalizedPath: normalizedPath === '' ? null : normalizedPath,
86
+ paramsSummary: parsedParams,
87
+ outcome,
88
+ });
89
+ }
90
+ return {
91
+ status: 'available',
92
+ truncated,
93
+ calls: records,
94
+ };
95
+ }
96
+ // ── buildProductionRuleContext ─────────────────────────────────────────────
97
+ /**
98
+ * Build a RuleContextV2 from the production data source (TrajectoryDatabase).
99
+ *
100
+ * ERR-024: if the query or assembly fails, returns an unavailable context
101
+ * (fail-soft). Never throws.
102
+ *
103
+ * sameActionBlockCount is always null in this version (spec §5.4:
104
+ * session-tracker.blockedAttempts is a session total, not per-action).
105
+ */
106
+ export function buildProductionRuleContext(sessionId, targetPath, source, projectDir, limit = DEFAULT_HISTORY_LIMIT) {
107
+ // Guard: no session → no context
108
+ if (!sessionId || typeof sessionId !== 'string' || sessionId.length === 0) {
109
+ return UNAVAILABLE_RULE_CONTEXT;
110
+ }
111
+ try {
112
+ const result = source.getRuleHostContextRows(sessionId, limit);
113
+ const history = assembleHistoryFromRows(result.rows, result.truncated, projectDir);
114
+ // sameActionBlockCount = null (spec §5.4 — no reliable per-action source)
115
+ const facts = computeBehaviorFacts(history, targetPath, null);
116
+ return {
117
+ version: 2,
118
+ history,
119
+ facts,
120
+ };
121
+ }
122
+ catch {
123
+ // ERR-024: fail-soft — never let a DB error propagate to the rule host
124
+ return unavailableContext('query or assembly failed');
125
+ }
126
+ }
127
+ // ── internal helpers ───────────────────────────────────────────────────────
128
+ function unavailable(reason) {
129
+ return {
130
+ status: 'unavailable',
131
+ unavailableReason: reason,
132
+ truncated: false,
133
+ calls: [],
134
+ };
135
+ }
136
+ function unavailableContext(reason) {
137
+ return {
138
+ version: 2,
139
+ history: unavailable(reason),
140
+ facts: {
141
+ priorReadOfTarget: 'unknown',
142
+ readCount: null,
143
+ writeCount: null,
144
+ uniqueWritePathCount: null,
145
+ sameActionBlockCount: null,
146
+ },
147
+ };
148
+ }
@@ -10,7 +10,7 @@
10
10
  * BUILTIN_PATTERNS (engine) → detection regexes per model id
11
11
  * THINKING_MODELS (merged) → full definitions with patterns
12
12
  */
13
- export interface ThinkingModelDefinition {
13
+ interface ThinkingModelDefinition {
14
14
  id: string;
15
15
  name: string;
16
16
  description: string;
@@ -18,11 +18,11 @@ export interface ThinkingModelDefinition {
18
18
  patterns: RegExp[];
19
19
  baselineScenarios: string[];
20
20
  }
21
- export interface ThinkingModelMatch {
21
+ interface ThinkingModelMatch {
22
22
  modelId: string;
23
23
  matchedPattern: string;
24
24
  }
25
- export interface ThinkingScenarioContext {
25
+ interface ThinkingScenarioContext {
26
26
  recentToolCalls?: {
27
27
  toolName: string;
28
28
  outcome: 'success' | 'failure' | 'blocked';
@@ -51,20 +51,6 @@ export interface ThinkingScenarioContext {
51
51
  * @param workspaceDir Optional. If provided, loads from that workspace's THINKING_OS.md.
52
52
  */
53
53
  export declare function listThinkingModels(workspaceDir?: string): ThinkingModelDefinition[];
54
- /**
55
- * Clear the cached model definitions.
56
- * Call this when THINKING_OS.md changes.
57
- */
58
- export declare function clearThinkingModelCache(): void;
59
- export declare function getThinkingModel(modelId: string, workspaceDir?: string): ThinkingModelDefinition | undefined;
60
54
  export declare function detectThinkingModelMatches(text: string, workspaceDir?: string): ThinkingModelMatch[];
61
- /**
62
- * Get all model definitions for display purposes (no patterns).
63
- */
64
- export declare function getThinkingModelDefinitions(workspaceDir?: string): {
65
- modelId: string;
66
- name: string;
67
- description: string;
68
- antiPattern?: string;
69
- }[];
70
55
  export declare function deriveThinkingScenarios(modelId: string, context: ThinkingScenarioContext): string[];
56
+ export {};
@@ -213,15 +213,7 @@ export function listThinkingModels(workspaceDir) {
213
213
  _cachedWorkspace = cacheKey;
214
214
  return models.slice();
215
215
  }
216
- /**
217
- * Clear the cached model definitions.
218
- * Call this when THINKING_OS.md changes.
219
- */
220
- export function clearThinkingModelCache() {
221
- _cachedDefinitions = null;
222
- _cachedWorkspace = null;
223
- }
224
- export function getThinkingModel(modelId, workspaceDir) {
216
+ function getThinkingModel(modelId, workspaceDir) {
225
217
  const models = listThinkingModels(workspaceDir);
226
218
  return models.find(m => m.id === modelId);
227
219
  }
@@ -243,17 +235,6 @@ export function detectThinkingModelMatches(text, workspaceDir) {
243
235
  }
244
236
  return matches;
245
237
  }
246
- /**
247
- * Get all model definitions for display purposes (no patterns).
248
- */
249
- export function getThinkingModelDefinitions(workspaceDir) {
250
- return listThinkingModels(workspaceDir).map(m => ({
251
- modelId: m.id,
252
- name: m.id + ': ' + m.name,
253
- description: m.description,
254
- antiPattern: m.antiPattern,
255
- }));
256
- }
257
238
  export function deriveThinkingScenarios(modelId, context) {
258
239
  const scenarios = new Set(getThinkingModel(modelId)?.baselineScenarios ?? []);
259
240
  if ((context.recentToolCalls ?? []).some((call) => call.outcome === 'failure')) {
@@ -222,4 +222,22 @@ export interface TrajectoryDatabaseOptions {
222
222
  busyTimeoutMs?: number;
223
223
  orphanBlobGraceDays?: number;
224
224
  }
225
+ /**
226
+ * Raw row from tool_calls for RuleContext v2 history assembly.
227
+ * Fields are raw strings from SQLite — the assembler validates them as unknown.
228
+ */
229
+ export interface RuleHostContextRow {
230
+ readonly id: number;
231
+ readonly toolName: string;
232
+ readonly outcome: string;
233
+ readonly paramsJson: string;
234
+ }
235
+ /**
236
+ * Result of getRuleHostContextRows: FIFO-ordered rows + truncated flag.
237
+ * truncated=true when more than `limit` rows existed (limit+1 read trick).
238
+ */
239
+ export interface RuleHostContextResult {
240
+ readonly rows: readonly RuleHostContextRow[];
241
+ readonly truncated: boolean;
242
+ }
225
243
  export type { DailyMetricRow };
@@ -1,5 +1,5 @@
1
- import type { CorrectionSampleReviewStatus, CorrectionExportMode, TrajectoryDataStats, TrajectoryAssistantTurnInput, TrajectoryUserTurnInput, TrajectoryToolCallInput, TrajectoryPainEventInput, TrajectoryGateBlockInput, TrajectoryTrustChangeInput, TrajectoryPrincipleEventInput, TrajectoryTaskOutcomeInput, TrajectorySessionInput, EvolutionTaskInput, EvolutionEventInput, EvolutionTaskRecord, EvolutionEventRecord, EvolutionTaskFilters, AssistantTurnRecord, CorrectionSampleRecord, TrajectoryExportResult, TrajectoryDatabaseOptions } from './trajectory-types.js';
2
- export type { CorrectionSampleReviewStatus, CorrectionExportMode, TrajectoryDataStats, TrajectoryAssistantTurnInput, TrajectoryUserTurnInput, TrajectoryToolCallInput, TrajectoryPainEventInput, TrajectoryGateBlockInput, DailyMetricRow, TrajectoryTrustChangeInput, TrajectoryPrincipleEventInput, TrajectoryTaskOutcomeInput, TrajectorySessionInput, TaskKind, TaskPriority, EvolutionTaskInput, EvolutionEventInput, EvolutionTaskRecord, EvolutionEventRecord, EvolutionTaskFilters, AssistantTurnRecord, CorrectionSampleRecord, TrajectoryExportResult, TrajectoryDatabaseOptions, } from './trajectory-types.js';
1
+ import type { CorrectionSampleReviewStatus, CorrectionExportMode, TrajectoryDataStats, TrajectoryAssistantTurnInput, TrajectoryUserTurnInput, TrajectoryToolCallInput, TrajectoryPainEventInput, TrajectoryGateBlockInput, TrajectoryTrustChangeInput, TrajectoryPrincipleEventInput, TrajectoryTaskOutcomeInput, TrajectorySessionInput, EvolutionTaskInput, EvolutionEventInput, EvolutionTaskRecord, EvolutionEventRecord, EvolutionTaskFilters, AssistantTurnRecord, CorrectionSampleRecord, TrajectoryExportResult, TrajectoryDatabaseOptions, RuleHostContextResult } from './trajectory-types.js';
2
+ export type { CorrectionSampleReviewStatus, CorrectionExportMode, TrajectoryDataStats, TrajectoryAssistantTurnInput, TrajectoryUserTurnInput, TrajectoryToolCallInput, TrajectoryPainEventInput, TrajectoryGateBlockInput, DailyMetricRow, TrajectoryTrustChangeInput, TrajectoryPrincipleEventInput, TrajectoryTaskOutcomeInput, TrajectorySessionInput, TaskKind, TaskPriority, EvolutionTaskInput, EvolutionEventInput, EvolutionTaskRecord, EvolutionEventRecord, EvolutionTaskFilters, AssistantTurnRecord, CorrectionSampleRecord, TrajectoryExportResult, TrajectoryDatabaseOptions, RuleHostContextRow, RuleHostContextResult, } from './trajectory-types.js';
3
3
  /**
4
4
  * Initialize trajectory.db schema at the given workspace directory.
5
5
  *
@@ -121,6 +121,15 @@ export declare class TrajectoryDatabase {
121
121
  resultPreview: string | null;
122
122
  createdAt: string;
123
123
  }[];
124
+ /**
125
+ * PRI-482 Phase 3: Query tool_calls for RuleContext v2 history assembly.
126
+ *
127
+ * Reads limit+1 rows (DESC by id) to compute truncated, then reverses to FIFO.
128
+ * Returns raw rows — the assembler (rule-context-assembler.ts) validates them.
129
+ *
130
+ * Spec: §5.1, §5.2. ERR-026: reuses production schema (no hand-written DDL).
131
+ */
132
+ getRuleHostContextRows(sessionId: string, limit?: number): RuleHostContextResult;
124
133
  /**
125
134
  * List pain events for a session.
126
135
  *
@@ -903,6 +903,36 @@ export class TrajectoryDatabase {
903
903
  };
904
904
  });
905
905
  }
906
+ /**
907
+ * PRI-482 Phase 3: Query tool_calls for RuleContext v2 history assembly.
908
+ *
909
+ * Reads limit+1 rows (DESC by id) to compute truncated, then reverses to FIFO.
910
+ * Returns raw rows — the assembler (rule-context-assembler.ts) validates them.
911
+ *
912
+ * Spec: §5.1, §5.2. ERR-026: reuses production schema (no hand-written DDL).
913
+ */
914
+ getRuleHostContextRows(sessionId, limit = 20) {
915
+ const cappedLimit = Math.max(1, Math.floor(limit));
916
+ const rows = this.db.prepare(`
917
+ SELECT id, tool_name, outcome, params_json
918
+ FROM tool_calls
919
+ WHERE session_id = ?
920
+ ORDER BY id DESC
921
+ LIMIT ?
922
+ `).all(sessionId, cappedLimit + 1);
923
+ const truncated = rows.length > cappedLimit;
924
+ const output = truncated ? rows.slice(0, cappedLimit) : rows;
925
+ output.reverse(); // DESC → FIFO (oldest first)
926
+ return {
927
+ rows: output.map((row) => ({
928
+ id: Number(row.id),
929
+ toolName: String(row.tool_name),
930
+ outcome: String(row.outcome),
931
+ paramsJson: String(row.params_json),
932
+ })),
933
+ truncated,
934
+ };
935
+ }
906
936
  /**
907
937
  * List pain events for a session.
908
938
  *
@@ -2,7 +2,7 @@
2
2
  "id": "principles-disciple",
3
3
  "name": "Principles Disciple",
4
4
  "description": "Evolutionary programming agent framework with strategic guardrails and reflection loops.",
5
- "version": "1.156.0",
5
+ "version": "1.158.0",
6
6
  "activation": {
7
7
  "onCapabilities": [
8
8
  "hook"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "principles-disciple",
3
- "version": "1.156.0",
3
+ "version": "1.158.0",
4
4
  "description": "Native OpenClaw plugin for Principles Disciple",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,5 +0,0 @@
1
- export interface MigrationResult {
2
- importedEvents: number;
3
- streamPath: string;
4
- }
5
- export declare function migrateLegacyEvolutionData(workspaceDir: string): MigrationResult;
@@ -1,65 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import { stableContentHash } from './evolution-reducer.js';
4
- import { SystemLogger } from './system-logger.js';
5
- function appendEvent(streamPath, event) {
6
- fs.appendFileSync(streamPath, `${JSON.stringify(event)}\n`, 'utf8');
7
- }
8
- function loadImportedHashes(streamPath, workspaceDir) {
9
- if (!fs.existsSync(streamPath))
10
- return new Set();
11
- const raw = fs.readFileSync(streamPath, 'utf8').trim();
12
- if (!raw)
13
- return new Set();
14
- const hashes = new Set();
15
- for (const line of raw.split('\n')) {
16
- try {
17
- const event = JSON.parse(line);
18
- if (event.type !== 'legacy_import')
19
- continue;
20
- const hash = event.data.contentHash;
21
- if (typeof hash === 'string')
22
- hashes.add(hash);
23
- }
24
- catch (e) {
25
- SystemLogger.log(workspaceDir, 'MIGRATION_WARN', `skip malformed line: ${String(e)}`);
26
- }
27
- }
28
- return hashes;
29
- }
30
- export function migrateLegacyEvolutionData(workspaceDir) {
31
- const streamPath = path.join(workspaceDir, 'memory', 'evolution.jsonl');
32
- fs.mkdirSync(path.dirname(streamPath), { recursive: true });
33
- const candidates = [
34
- path.join(workspaceDir, 'memory', 'ISSUE_LOG.md'),
35
- path.join(workspaceDir, 'memory', 'DECISIONS.md'),
36
- path.join(workspaceDir, '.principles', 'PRINCIPLES.md'),
37
- ];
38
- const existingHashes = loadImportedHashes(streamPath, workspaceDir);
39
- let importedEvents = 0;
40
- for (const sourceFile of candidates) {
41
- if (!fs.existsSync(sourceFile)) {
42
- continue;
43
- }
44
- const content = fs.readFileSync(sourceFile, 'utf8').trim();
45
- if (!content) {
46
- continue;
47
- }
48
- const contentHash = stableContentHash(`${sourceFile}:${content}`);
49
- if (existingHashes.has(contentHash)) {
50
- continue;
51
- }
52
- appendEvent(streamPath, {
53
- ts: new Date().toISOString(),
54
- type: 'legacy_import',
55
- data: {
56
- sourceFile: path.relative(workspaceDir, sourceFile),
57
- content,
58
- contentHash,
59
- },
60
- });
61
- importedEvents += 1;
62
- existingHashes.add(contentHash);
63
- }
64
- return { importedEvents, streamPath };
65
- }
@@ -1,8 +0,0 @@
1
- /**
2
- * Rule Host Helpers — Re-exported from @principles/core (PRI-42)
3
- *
4
- * Canonical definitions moved to:
5
- * packages/principles-core/src/runtime-v2/internalization/rule-host-helpers.ts
6
- */
7
- export type { RuleHostHelpers } from '@principles/core/runtime-v2';
8
- export { createRuleHostHelpers } from '@principles/core/runtime-v2';
@@ -1,7 +0,0 @@
1
- /**
2
- * Rule Host Helpers — Re-exported from @principles/core (PRI-42)
3
- *
4
- * Canonical definitions moved to:
5
- * packages/principles-core/src/runtime-v2/internalization/rule-host-helpers.ts
6
- */
7
- export { createRuleHostHelpers } from '@principles/core/runtime-v2';
@@ -1,7 +0,0 @@
1
- /**
2
- * Rule Host Types — Re-exported from @principles/core (PRI-42)
3
- *
4
- * Canonical definitions moved to:
5
- * packages/principles-core/src/runtime-v2/internalization/rule-host-contracts.ts
6
- */
7
- export type { RuleHostInput, RuleHostDecision, RuleHostMeta, RuleHostResult, LoadedImplementation, } from '@principles/core/runtime-v2';
@@ -1,7 +0,0 @@
1
- /**
2
- * Rule Host Types — Re-exported from @principles/core (PRI-42)
3
- *
4
- * Canonical definitions moved to:
5
- * packages/principles-core/src/runtime-v2/internalization/rule-host-contracts.ts
6
- */
7
- export {};