pi-harness-runtime 1.1.164 → 1.1.165

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 (111) hide show
  1. package/package.json +1 -1
  2. package/packages/a2a-adapter/package.json +1 -1
  3. package/packages/agent-policy/package.json +1 -1
  4. package/packages/architecture-generator/package.json +1 -1
  5. package/packages/auth/package.json +1 -1
  6. package/packages/autonomous-refactor/package.json +1 -1
  7. package/packages/autonomous-runtime/package.json +1 -1
  8. package/packages/cache-strategy/package.json +1 -1
  9. package/packages/capability-registry/package.json +1 -1
  10. package/packages/checkpoint/package.json +1 -1
  11. package/packages/cli-plugin-sdk/package.json +1 -1
  12. package/packages/clipboard/package.json +1 -1
  13. package/packages/clipboard-plugin/package.json +1 -1
  14. package/packages/code-generation/package.json +1 -1
  15. package/packages/code-review/package.json +1 -1
  16. package/packages/codex-adapter/package.json +1 -1
  17. package/packages/codex-watcher/package.json +1 -1
  18. package/packages/config-capture/package.json +1 -1
  19. package/packages/context-compiler/package.json +1 -1
  20. package/packages/context-discovery/package.json +1 -1
  21. package/packages/context-manager/package.json +1 -1
  22. package/packages/cookie-sanitizer/package.json +1 -1
  23. package/packages/cost-optimizer/package.json +1 -1
  24. package/packages/dependency-analyzer/package.json +1 -1
  25. package/packages/django-plugin/package.json +1 -1
  26. package/packages/doc-generator/package.json +1 -1
  27. package/packages/evaluation-engine/package.json +1 -1
  28. package/packages/evaluation-runner/package.json +1 -1
  29. package/packages/event-bus/package.json +1 -1
  30. package/packages/event-store/package.json +1 -1
  31. package/packages/experience-replay/package.json +1 -1
  32. package/packages/feedback-collector/package.json +1 -1
  33. package/packages/file-copy-helper/package.json +1 -1
  34. package/packages/framework-detector/package.json +1 -1
  35. package/packages/framework-plugin-sdk/package.json +1 -1
  36. package/packages/frappe-plugin/package.json +1 -1
  37. package/packages/generic-web-plugin/package.json +1 -1
  38. package/packages/health-monitor/package.json +1 -1
  39. package/packages/intent-analyzer/package.json +1 -1
  40. package/packages/jev-judge/package.json +1 -1
  41. package/packages/knowledge-graph/package.json +1 -1
  42. package/packages/knowledge-retrieval/package.json +1 -1
  43. package/packages/laravel-plugin/package.json +1 -1
  44. package/packages/learning-engine/package.json +1 -1
  45. package/packages/mcp-adapter/package.json +1 -1
  46. package/packages/memory-engine/package.json +1 -1
  47. package/packages/milestone-manager/package.json +1 -1
  48. package/packages/model-registry/package.json +1 -1
  49. package/packages/nextjs-plugin/package.json +1 -1
  50. package/packages/notification/dist/adapters/telegram-adapter.d.ts.map +1 -1
  51. package/packages/notification/dist/adapters/telegram-adapter.js +13 -0
  52. package/packages/notification/dist/adapters/telegram-adapter.js.map +1 -1
  53. package/packages/notification/dist/index.d.ts +1 -0
  54. package/packages/notification/dist/index.d.ts.map +1 -1
  55. package/packages/notification/dist/index.js +1 -0
  56. package/packages/notification/dist/index.js.map +1 -1
  57. package/packages/notification/dist/telegram-content-filter.d.ts +36 -0
  58. package/packages/notification/dist/telegram-content-filter.d.ts.map +1 -0
  59. package/packages/notification/dist/telegram-content-filter.js +158 -0
  60. package/packages/notification/dist/telegram-content-filter.js.map +1 -0
  61. package/packages/notification/package.json +1 -1
  62. package/packages/notification/src/adapters/telegram-adapter.ts +16 -0
  63. package/packages/notification/src/index.ts +4 -0
  64. package/packages/notification/src/telegram-content-filter.ts +207 -0
  65. package/packages/observability/package.json +1 -1
  66. package/packages/okf-indexer/package.json +1 -1
  67. package/packages/paperclip-integration/package.json +1 -1
  68. package/packages/performance-optimizer/package.json +1 -1
  69. package/packages/privilege-broker/package.json +1 -1
  70. package/packages/project-analyzer/package.json +1 -1
  71. package/packages/project-bootstrap/package.json +1 -1
  72. package/packages/projection-engine/package.json +1 -1
  73. package/packages/prompt-compiler/package.json +1 -1
  74. package/packages/prompt-versioning/package.json +1 -1
  75. package/packages/provider-adapter-sdk/package.json +1 -1
  76. package/packages/provider-router/package.json +1 -1
  77. package/packages/provider-selector/package.json +1 -1
  78. package/packages/providers/package.json +1 -1
  79. package/packages/qdrant-skills/package.json +1 -1
  80. package/packages/quota-manager/package.json +1 -1
  81. package/packages/rate-limiter/package.json +1 -1
  82. package/packages/react-vite-plugin/package.json +1 -1
  83. package/packages/release-manager/package.json +1 -1
  84. package/packages/requirement-compiler/package.json +1 -1
  85. package/packages/runtime/package.json +1 -1
  86. package/packages/scheduler/package.json +1 -1
  87. package/packages/scheduler-adapter/package.json +1 -1
  88. package/packages/session/package.json +1 -1
  89. package/packages/session-api/package.json +1 -1
  90. package/packages/session-export/package.json +1 -1
  91. package/packages/shared-context/package.json +1 -1
  92. package/packages/skill-mcp-client/package.json +1 -1
  93. package/packages/skill-registry/package.json +1 -1
  94. package/packages/skills/package.json +1 -1
  95. package/packages/sprint-planner/package.json +1 -1
  96. package/packages/subscription-engine/package.json +1 -1
  97. package/packages/task-compiler/package.json +1 -1
  98. package/packages/tencentdb-memory/package.json +1 -1
  99. package/packages/tencentdb-sync/package.json +1 -1
  100. package/packages/test-data-generator/package.json +1 -1
  101. package/packages/test-generator/package.json +1 -1
  102. package/packages/todo-bd-sync/package.json +1 -1
  103. package/packages/token-estimation/package.json +1 -1
  104. package/packages/token-optimizer/package.json +1 -1
  105. package/packages/trajectory/package.json +1 -1
  106. package/packages/tui/package.json +1 -1
  107. package/packages/types/package.json +1 -1
  108. package/packages/workflow-events/package.json +1 -1
  109. package/packages/workspace-scanner/package.json +1 -1
  110. package/packages/worktree/package.json +1 -1
  111. package/packages/write-review/package.json +1 -1
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Telegram Content Filter — Jev-Powered Sensitive Content Detection
3
+ *
4
+ * Before sending to Telegram, this module analyzes content for sensitive
5
+ * credentials that should NOT be transmitted.
6
+ */
7
+ /**
8
+ * Extract text content from notification payload
9
+ */
10
+ function extractContent(payload) {
11
+ const parts = [];
12
+ if (payload.title)
13
+ parts.push(payload.title);
14
+ if (payload.message)
15
+ parts.push(payload.message);
16
+ if (payload.details) {
17
+ for (const [key, value] of Object.entries(payload.details)) {
18
+ if (typeof value === "string" && value) {
19
+ parts.push(value);
20
+ }
21
+ }
22
+ }
23
+ return parts.join("\n");
24
+ }
25
+ /**
26
+ * Simple heuristic check for obvious credentials (fallback when Jev unavailable)
27
+ */
28
+ function heuristicCheck(content) {
29
+ // Check for common credential patterns
30
+ const sensitivePatterns = [
31
+ /\d+:[-A-Za-z0-9_]{35,}/, // Telegram bot token
32
+ /sk-[a-zA-Z0-9]{20,}/, // OpenAI-style keys
33
+ /[a-zA-Z0-9_-]{32,}/, // Long API keys
34
+ /Bearer\s+[a-zA-Z0-9_-]{20,}/i, // Bearer tokens
35
+ /(api[_-]?key|secret|token|password|credential)[=:\s]+[^\s]{16,}/i,
36
+ /-----BEGIN\s+(RSA|EC|OPENSSH|DSA)\s+PRIVATE\s+KEY-----/,
37
+ /infisical[_-]?(client[_-])?(secret|id)/i,
38
+ ];
39
+ for (const pattern of sensitivePatterns) {
40
+ if (pattern.test(content)) {
41
+ let matchType = "api_key_or_secret";
42
+ if (/\d+:[-A-Za-z0-9_]{35,}/.test(content)) {
43
+ matchType = "telegram_bot_token";
44
+ }
45
+ else if (/sk-[a-zA-Z0-9]{20,}/.test(content)) {
46
+ matchType = "openai_style_key";
47
+ }
48
+ else if (/-----BEGIN\s+(RSA|EC|OPENSSH|DSA)\s+PRIVATE\s+KEY-----/.test(content)) {
49
+ matchType = "private_key";
50
+ }
51
+ else if (/infisical/i.test(content)) {
52
+ matchType = "infisical_credential";
53
+ }
54
+ return {
55
+ shouldSend: false,
56
+ judgment: "BLOCK",
57
+ reason: `Sensitive content detected: ${matchType}`,
58
+ confidence: 0.95,
59
+ };
60
+ }
61
+ }
62
+ return {
63
+ shouldSend: true,
64
+ judgment: "SEND",
65
+ confidence: 0.9,
66
+ };
67
+ }
68
+ /**
69
+ * Call Jev to analyze content for sensitive credentials
70
+ */
71
+ async function callJev(content) {
72
+ // Get API key from environment
73
+ const apiKey = process.env.TYPESAFE_API_KEY;
74
+ if (!apiKey) {
75
+ return null; // Jev not available
76
+ }
77
+ try {
78
+ const response = await fetch("https://api.typesafe.ai/v1/jev/decide", {
79
+ method: "POST",
80
+ headers: {
81
+ "Content-Type": "application/json",
82
+ Authorization: `Bearer ${apiKey}`,
83
+ },
84
+ body: JSON.stringify({
85
+ question: "Does this message contain any API keys, tokens, secrets, passwords, or other sensitive credentials that should NOT be sent to a third-party service like Telegram? Answer only YES or NO.",
86
+ context: {
87
+ message: content,
88
+ action: "telegram_notification",
89
+ security_level: "high",
90
+ },
91
+ }),
92
+ });
93
+ if (!response.ok) {
94
+ return null;
95
+ }
96
+ const data = (await response.json());
97
+ const decision = data.decision?.toUpperCase() ?? "UNKNOWN";
98
+ const isSensitive = decision === "YES" || decision === "BLOCK";
99
+ return {
100
+ shouldSend: !isSensitive,
101
+ judgment: isSensitive ? "BLOCK" : "SEND",
102
+ reason: data.reasoning,
103
+ confidence: data.confidence,
104
+ };
105
+ }
106
+ catch {
107
+ return null;
108
+ }
109
+ }
110
+ /**
111
+ * Filter notification content before sending to Telegram
112
+ *
113
+ * @param payload - The notification payload
114
+ * @returns Filter result with decision
115
+ */
116
+ export async function filterTelegramContent(payload) {
117
+ const content = extractContent(payload);
118
+ // First, run heuristic check (always available)
119
+ const heuristicResult = heuristicCheck(content);
120
+ if (heuristicResult.judgment === "BLOCK") {
121
+ logFilterDecision("BLOCK", heuristicResult.reason ?? "Heuristic match", payload.jobId);
122
+ return heuristicResult;
123
+ }
124
+ // Try Jev if available
125
+ const jevResult = await callJev(content);
126
+ if (jevResult) {
127
+ if (jevResult.judgment === "BLOCK") {
128
+ logFilterDecision("BLOCK", jevResult.reason ?? "Jev judgment", payload.jobId);
129
+ }
130
+ return jevResult;
131
+ }
132
+ // Jev unavailable - use heuristic result
133
+ return heuristicResult;
134
+ }
135
+ /**
136
+ * Log filter decision securely (no content, just metadata)
137
+ */
138
+ function logFilterDecision(decision, reason, jobId) {
139
+ console.warn(`[TelegramFilter] ${decision} job=${jobId} reason=${reason} channel=telegram`);
140
+ }
141
+ /**
142
+ * Mask a value safely for logging
143
+ */
144
+ function maskValue(value) {
145
+ if (!value || value.length <= 8)
146
+ return "****";
147
+ return `${value.slice(0, 4)}...${value.slice(-4)}`;
148
+ }
149
+ /**
150
+ * Get safe notification version (masked) for logging
151
+ */
152
+ export function getSafeNotificationLog(payload) {
153
+ const maskedTitle = maskValue(payload.title);
154
+ const maskedMessage = maskValue(payload.message);
155
+ const maskedJobId = maskValue(payload.jobId);
156
+ return `event=${payload.event} job=${maskedJobId} title=${maskedTitle} message_len=${maskedMessage.length}`;
157
+ }
158
+ //# sourceMappingURL=telegram-content-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram-content-filter.js","sourceRoot":"","sources":["../src/telegram-content-filter.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/notification",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "pi-harness-runtime — notification",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,6 +19,7 @@ import type {
19
19
  } from "../types.js";
20
20
  import { BaseChannelAdapter } from "../base-adapter.js";
21
21
  import { maskString } from "../mask-secrets.js";
22
+ import { filterTelegramContent, getSafeNotificationLog } from "../telegram-content-filter.js";
22
23
 
23
24
  export class TelegramAdapter extends BaseChannelAdapter {
24
25
  readonly id = "telegram";
@@ -71,6 +72,21 @@ return this._botUsername;
71
72
  */
72
73
  async send(payload: NotificationPayload): Promise<NotificationResult> {
73
74
  try {
75
+ // SECURITY: Jev-powered content filter - block if contains sensitive credentials
76
+ const filterResult = await filterTelegramContent(payload);
77
+ if (!filterResult.shouldSend) {
78
+ console.warn(
79
+ `[TelegramAdapter] BLOCKED by Jev judgment=${filterResult.judgment} reason=${filterResult.reason} job=${payload.jobId}`,
80
+ );
81
+ // Log safe notification metadata (no secrets)
82
+ console.log(`[TelegramAdapter] ${getSafeNotificationLog(payload)}`);
83
+ return {
84
+ success: false,
85
+ channel: this.id,
86
+ error: "Content blocked: sensitive credentials detected",
87
+ };
88
+ }
89
+
74
90
  const cfg = this.config.config as TelegramConfig;
75
91
  const message = this.formatMessage(payload);
76
92
 
@@ -17,3 +17,7 @@ export {
17
17
  maskPayload,
18
18
  isSensitiveField,
19
19
  } from "./mask-secrets.js";
20
+ export {
21
+ filterTelegramContent,
22
+ getSafeNotificationLog,
23
+ } from "./telegram-content-filter.js";
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Telegram Content Filter — Jev-Powered Sensitive Content Detection
3
+ *
4
+ * Before sending to Telegram, this module analyzes content for sensitive
5
+ * credentials that should NOT be transmitted.
6
+ */
7
+
8
+ import type { NotificationPayload } from "./types.js";
9
+
10
+ /**
11
+ * Jev judgment result for content filtering
12
+ */
13
+ export type ContentJudgment = "SEND" | "BLOCK" | "UNKNOWN";
14
+
15
+ /**
16
+ * Result of content filtering decision
17
+ */
18
+ export interface FilterResult {
19
+ /** Whether to send to Telegram */
20
+ shouldSend: boolean;
21
+ /** Jev's judgment */
22
+ judgment: ContentJudgment;
23
+ /** Reason for blocking (if blocked) */
24
+ reason?: string;
25
+ /** Confidence score (0-1) */
26
+ confidence?: number;
27
+ }
28
+
29
+ /**
30
+ * Extract text content from notification payload
31
+ */
32
+ function extractContent(payload: NotificationPayload): string {
33
+ const parts: string[] = [];
34
+
35
+ if (payload.title) parts.push(payload.title);
36
+ if (payload.message) parts.push(payload.message);
37
+
38
+ if (payload.details) {
39
+ for (const [key, value] of Object.entries(payload.details)) {
40
+ if (typeof value === "string" && value) {
41
+ parts.push(value);
42
+ }
43
+ }
44
+ }
45
+
46
+ return parts.join("\n");
47
+ }
48
+
49
+ /**
50
+ * Simple heuristic check for obvious credentials (fallback when Jev unavailable)
51
+ */
52
+ function heuristicCheck(content: string): FilterResult {
53
+ // Check for common credential patterns
54
+ const sensitivePatterns = [
55
+ /\d+:[-A-Za-z0-9_]{35,}/, // Telegram bot token
56
+ /sk-[a-zA-Z0-9]{20,}/, // OpenAI-style keys
57
+ /[a-zA-Z0-9_-]{32,}/, // Long API keys
58
+ /Bearer\s+[a-zA-Z0-9_-]{20,}/i, // Bearer tokens
59
+ /(api[_-]?key|secret|token|password|credential)[=:\s]+[^\s]{16,}/i,
60
+ /-----BEGIN\s+(RSA|EC|OPENSSH|DSA)\s+PRIVATE\s+KEY-----/,
61
+ /infisical[_-]?(client[_-])?(secret|id)/i,
62
+ ];
63
+
64
+ for (const pattern of sensitivePatterns) {
65
+ if (pattern.test(content)) {
66
+ let matchType = "api_key_or_secret";
67
+ if (/\d+:[-A-Za-z0-9_]{35,}/.test(content)) {
68
+ matchType = "telegram_bot_token";
69
+ } else if (/sk-[a-zA-Z0-9]{20,}/.test(content)) {
70
+ matchType = "openai_style_key";
71
+ } else if (/-----BEGIN\s+(RSA|EC|OPENSSH|DSA)\s+PRIVATE\s+KEY-----/.test(content)) {
72
+ matchType = "private_key";
73
+ } else if (/infisical/i.test(content)) {
74
+ matchType = "infisical_credential";
75
+ }
76
+
77
+ return {
78
+ shouldSend: false,
79
+ judgment: "BLOCK",
80
+ reason: `Sensitive content detected: ${matchType}`,
81
+ confidence: 0.95,
82
+ };
83
+ }
84
+ }
85
+
86
+ return {
87
+ shouldSend: true,
88
+ judgment: "SEND",
89
+ confidence: 0.9,
90
+ };
91
+ }
92
+
93
+ /**
94
+ * Call Jev to analyze content for sensitive credentials
95
+ */
96
+ async function callJev(content: string): Promise<FilterResult | null> {
97
+ // Get API key from environment
98
+ const apiKey = process.env.TYPESAFE_API_KEY;
99
+
100
+ if (!apiKey) {
101
+ return null; // Jev not available
102
+ }
103
+
104
+ try {
105
+ const response = await fetch("https://api.typesafe.ai/v1/jev/decide", {
106
+ method: "POST",
107
+ headers: {
108
+ "Content-Type": "application/json",
109
+ Authorization: `Bearer ${apiKey}`,
110
+ },
111
+ body: JSON.stringify({
112
+ question:
113
+ "Does this message contain any API keys, tokens, secrets, passwords, or other sensitive credentials that should NOT be sent to a third-party service like Telegram? Answer only YES or NO.",
114
+ context: {
115
+ message: content,
116
+ action: "telegram_notification",
117
+ security_level: "high",
118
+ },
119
+ }),
120
+ });
121
+
122
+ if (!response.ok) {
123
+ return null;
124
+ }
125
+
126
+ const data = (await response.json()) as {
127
+ decision?: string;
128
+ confidence?: number;
129
+ reasoning?: string;
130
+ };
131
+
132
+ const decision = data.decision?.toUpperCase() ?? "UNKNOWN";
133
+ const isSensitive = decision === "YES" || decision === "BLOCK";
134
+
135
+ return {
136
+ shouldSend: !isSensitive,
137
+ judgment: isSensitive ? "BLOCK" : "SEND",
138
+ reason: data.reasoning,
139
+ confidence: data.confidence,
140
+ };
141
+ } catch {
142
+ return null;
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Filter notification content before sending to Telegram
148
+ *
149
+ * @param payload - The notification payload
150
+ * @returns Filter result with decision
151
+ */
152
+ export async function filterTelegramContent(
153
+ payload: NotificationPayload,
154
+ ): Promise<FilterResult> {
155
+ const content = extractContent(payload);
156
+
157
+ // First, run heuristic check (always available)
158
+ const heuristicResult = heuristicCheck(content);
159
+ if (heuristicResult.judgment === "BLOCK") {
160
+ logFilterDecision("BLOCK", heuristicResult.reason ?? "Heuristic match", payload.jobId);
161
+ return heuristicResult;
162
+ }
163
+
164
+ // Try Jev if available
165
+ const jevResult = await callJev(content);
166
+ if (jevResult) {
167
+ if (jevResult.judgment === "BLOCK") {
168
+ logFilterDecision("BLOCK", jevResult.reason ?? "Jev judgment", payload.jobId);
169
+ }
170
+ return jevResult;
171
+ }
172
+
173
+ // Jev unavailable - use heuristic result
174
+ return heuristicResult;
175
+ }
176
+
177
+ /**
178
+ * Log filter decision securely (no content, just metadata)
179
+ */
180
+ function logFilterDecision(
181
+ decision: ContentJudgment,
182
+ reason: string,
183
+ jobId: string,
184
+ ): void {
185
+ console.warn(
186
+ `[TelegramFilter] ${decision} job=${jobId} reason=${reason} channel=telegram`,
187
+ );
188
+ }
189
+
190
+ /**
191
+ * Mask a value safely for logging
192
+ */
193
+ function maskValue(value: string): string {
194
+ if (!value || value.length <= 8) return "****";
195
+ return `${value.slice(0, 4)}...${value.slice(-4)}`;
196
+ }
197
+
198
+ /**
199
+ * Get safe notification version (masked) for logging
200
+ */
201
+ export function getSafeNotificationLog(payload: NotificationPayload): string {
202
+ const maskedTitle = maskValue(payload.title);
203
+ const maskedMessage = maskValue(payload.message);
204
+ const maskedJobId = maskValue(payload.jobId);
205
+
206
+ return `event=${payload.event} job=${maskedJobId} title=${maskedTitle} message_len=${maskedMessage.length}`;
207
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/observability",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Comprehensive observability system with structured logging, tracing, metrics, and alerting",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/okf-indexer",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "OKF document indexer for SKILL.md files (RFC-0106)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/paperclip-integration",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Paperclip API integration: poll agent inbox, route tasks to pi.dev, Telegram alerts",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/performance-optimizer",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Performance optimization analyzer for pi-harness runtime (RFC-0056)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/privilege-broker",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Privilege Broker — capability registry and command executor for autonomous runtime (RFC-0101 §7)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/project-analyzer",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Project analysis system for pi-harness-runtime - detects frameworks, rules, and project structure",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/project-bootstrap",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Project scaffolding from typed ProjectSpec",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/projection-engine",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/prompt-compiler",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Deterministic prompt compiler for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/prompt-versioning",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "type": "module",
5
5
  "description": "Version control for prompts with rollback support (RFC-0019)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/provider-adapter-sdk",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "SDK for building, testing, and registering AI provider adapters",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/provider-router",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Enhanced Provider Router for pi-harness-runtime — intelligent routing with capabilities, costs, and quotas",
5
5
  "type": "module",
6
6
  "main": "./dist/provider-router.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/provider-selector",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Multi-Provider Selector — route requests by cost, quality, and latency (RFC-0012)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/providers",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "pi-harness-runtime — providers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/qdrant-skills",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Qdrant vector search integration for Hermes-style skill system",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/quota-manager",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "pi-harness-runtime — quota-manager",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/rate-limiter",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "type": "module",
5
5
  "description": "Rate limiting with token bucket algorithm (RFC-0023)",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/react-vite-plugin",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "React/Vite framework analysis — components, config, TypeScript setup",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/release-manager",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "type": "module",
5
5
  "description": "Release management for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/requirement-compiler",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Compiles raw requirements into validated CompiledRequirement with ambiguity detection and risk tagging",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/runtime",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "pi-harness-runtime — runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/scheduler",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "pi-harness-runtime — scheduler",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/scheduler-adapter",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Scheduler Adapter — cron, systemd, launchd, internal adapters for autonomous runtime (RFC-0101 §6)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/session",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "description": "Session management system for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/session-api",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/session-export",
3
- "version": "1.1.164",
3
+ "version": "1.1.165",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {