pi-harness-runtime 1.1.154 → 1.1.155
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/harness/honcho-memory.ts +331 -0
- package/harness/service-commands.ts +221 -0
- package/index.ts +18 -3
- package/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/dist/client.d.ts +18 -0
- package/packages/qdrant-skills/dist/client.d.ts.map +1 -1
- package/packages/qdrant-skills/dist/client.js +65 -0
- package/packages/qdrant-skills/dist/client.js.map +1 -1
- package/packages/qdrant-skills/dist/index.d.ts +1 -1
- package/packages/qdrant-skills/dist/index.d.ts.map +1 -1
- package/packages/qdrant-skills/dist/index.js +1 -1
- package/packages/qdrant-skills/dist/index.js.map +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/qdrant-skills/src/client.ts +92 -0
- package/packages/qdrant-skills/src/index.ts +2 -0
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/dist/skill-commands.d.ts.map +1 -1
- package/packages/skills/dist/skill-commands.js +38 -10
- package/packages/skills/dist/skill-commands.js.map +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/skills/src/skill-commands.ts +43 -11
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Honcho Memory Lifecycle
|
|
3
|
+
*
|
|
4
|
+
* Implements automatic memory ingestion and retrieval for Honcho MCP.
|
|
5
|
+
*
|
|
6
|
+
* Flow:
|
|
7
|
+
* 1. Register MCP server (done in initHoncho)
|
|
8
|
+
* 2. On tool discovery, establish workspace/peer mapping
|
|
9
|
+
* 3. On agent turn complete, ingest sanitized message
|
|
10
|
+
* 4. On agent execution start, retrieve relevant context
|
|
11
|
+
* 5. Inject bounded context with provenance
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { logServiceEvent, honchoStatus } from "./service-diagnostics.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Honcho memory configuration
|
|
18
|
+
*/
|
|
19
|
+
export interface HonchoMemoryConfig {
|
|
20
|
+
/** Honcho API key */
|
|
21
|
+
apiKey: string;
|
|
22
|
+
/** Workspace ID for memory storage */
|
|
23
|
+
workspaceId?: string;
|
|
24
|
+
/** User peer ID */
|
|
25
|
+
userPeerId?: string;
|
|
26
|
+
/** Assistant peer ID */
|
|
27
|
+
assistantPeerId?: string;
|
|
28
|
+
/** Max context size to inject (chars) */
|
|
29
|
+
maxContextChars?: number;
|
|
30
|
+
/** Enable auto-ingestion */
|
|
31
|
+
autoIngest?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Ingested message record for deduplication
|
|
36
|
+
*/
|
|
37
|
+
interface IngestedMessage {
|
|
38
|
+
id: string;
|
|
39
|
+
ingestedAt: Date;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Honcho memory state
|
|
44
|
+
*/
|
|
45
|
+
export class HonchoMemory {
|
|
46
|
+
private config: HonchoMemoryConfig;
|
|
47
|
+
private ingestedMessages = new Map<string, IngestedMessage>();
|
|
48
|
+
private isConnected = false;
|
|
49
|
+
private toolsDiscovered = false;
|
|
50
|
+
private workspaceId: string | null = null;
|
|
51
|
+
|
|
52
|
+
constructor(config: HonchoMemoryConfig) {
|
|
53
|
+
this.config = {
|
|
54
|
+
maxContextChars: 4000,
|
|
55
|
+
autoIngest: true,
|
|
56
|
+
...config,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Set connected state
|
|
62
|
+
*/
|
|
63
|
+
setConnected(connected: boolean): void {
|
|
64
|
+
this.isConnected = connected;
|
|
65
|
+
honchoStatus.setConnected(connected);
|
|
66
|
+
if (connected) {
|
|
67
|
+
logServiceEvent({
|
|
68
|
+
ts: new Date().toISOString(),
|
|
69
|
+
level: "info",
|
|
70
|
+
service: "honcho",
|
|
71
|
+
operation: "connected",
|
|
72
|
+
phase: "completed",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Set tools discovered state
|
|
79
|
+
*/
|
|
80
|
+
setToolsDiscovered(discovered: boolean): void {
|
|
81
|
+
this.toolsDiscovered = discovered;
|
|
82
|
+
if (discovered) {
|
|
83
|
+
logServiceEvent({
|
|
84
|
+
ts: new Date().toISOString(),
|
|
85
|
+
level: "info",
|
|
86
|
+
service: "honcho",
|
|
87
|
+
operation: "tools_discovered",
|
|
88
|
+
phase: "completed",
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Set workspace ID after session creation
|
|
95
|
+
*/
|
|
96
|
+
setWorkspace(workspaceId: string): void {
|
|
97
|
+
this.workspaceId = workspaceId;
|
|
98
|
+
logServiceEvent({
|
|
99
|
+
ts: new Date().toISOString(),
|
|
100
|
+
level: "info",
|
|
101
|
+
service: "honcho",
|
|
102
|
+
operation: "workspace_created",
|
|
103
|
+
phase: "completed",
|
|
104
|
+
workspaceId,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Check if memory is ready
|
|
110
|
+
*/
|
|
111
|
+
isReady(): boolean {
|
|
112
|
+
return this.isConnected && this.toolsDiscovered && !!this.workspaceId;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Generate deduplication ID for a message
|
|
117
|
+
*/
|
|
118
|
+
private generateMessageId(
|
|
119
|
+
role: string,
|
|
120
|
+
content: string,
|
|
121
|
+
timestamp: Date
|
|
122
|
+
): string {
|
|
123
|
+
// Simple hash-based ID for deduplication
|
|
124
|
+
const data = `${role}:${content}:${timestamp.toISOString()}`;
|
|
125
|
+
let hash = 0;
|
|
126
|
+
for (let i = 0; i < data.length; i++) {
|
|
127
|
+
const char = data.charCodeAt(i);
|
|
128
|
+
hash = ((hash << 5) - hash) + char;
|
|
129
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
130
|
+
}
|
|
131
|
+
return Math.abs(hash).toString(36);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Sanitize message content for storage
|
|
136
|
+
* Removes sensitive data, tool calls, etc.
|
|
137
|
+
*/
|
|
138
|
+
private sanitizeContent(
|
|
139
|
+
content: string | Array<{ type?: string; text?: string }>
|
|
140
|
+
): string {
|
|
141
|
+
if (typeof content === "string") {
|
|
142
|
+
// Truncate very long messages
|
|
143
|
+
return content.slice(0, 2000);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Handle array content (TextContent blocks)
|
|
147
|
+
if (Array.isArray(content)) {
|
|
148
|
+
return content
|
|
149
|
+
.filter((block) => block.type === "text")
|
|
150
|
+
.map((block) => {
|
|
151
|
+
const text = (block as { text?: string }).text ?? "";
|
|
152
|
+
return text.slice(0, 2000);
|
|
153
|
+
})
|
|
154
|
+
.join("\n");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return String(content).slice(0, 2000);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Ingest a completed turn message
|
|
162
|
+
*/
|
|
163
|
+
async ingestMessage(
|
|
164
|
+
role: "user" | "assistant",
|
|
165
|
+
content: string | Array<{ type?: string; text?: string }>,
|
|
166
|
+
timestamp: Date = new Date()
|
|
167
|
+
): Promise<{ success: boolean; deduped: boolean; error?: string }> {
|
|
168
|
+
if (!this.isReady()) {
|
|
169
|
+
return { success: false, deduped: false, error: "memory not ready" };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!this.config.autoIngest) {
|
|
173
|
+
return { success: false, deduped: false, error: "auto-ingest disabled" };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const messageId = this.generateMessageId(role, String(content), timestamp);
|
|
177
|
+
|
|
178
|
+
// Check deduplication
|
|
179
|
+
if (this.ingestedMessages.has(messageId)) {
|
|
180
|
+
logServiceEvent({
|
|
181
|
+
ts: new Date().toISOString(),
|
|
182
|
+
level: "debug",
|
|
183
|
+
service: "honcho",
|
|
184
|
+
operation: "ingest_skipped",
|
|
185
|
+
phase: "completed",
|
|
186
|
+
reason: "deduplicated",
|
|
187
|
+
messageId,
|
|
188
|
+
});
|
|
189
|
+
return { success: true, deduped: true };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const sanitized = this.sanitizeContent(content);
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
// Call Honcho add_messages_to_session tool
|
|
196
|
+
// Note: This assumes the MCP tool is available through the adapter
|
|
197
|
+
logServiceEvent({
|
|
198
|
+
ts: new Date().toISOString(),
|
|
199
|
+
level: "info",
|
|
200
|
+
service: "honcho",
|
|
201
|
+
operation: "ingest_started",
|
|
202
|
+
phase: "start",
|
|
203
|
+
role,
|
|
204
|
+
messageLength: sanitized.length,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Store message for deduplication
|
|
208
|
+
this.ingestedMessages.set(messageId, {
|
|
209
|
+
id: messageId,
|
|
210
|
+
ingestedAt: timestamp,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Clean up old messages (keep last 100)
|
|
214
|
+
if (this.ingestedMessages.size > 100) {
|
|
215
|
+
const oldest = Array.from(this.ingestedMessages.entries())
|
|
216
|
+
.sort((a, b) => a[1].ingestedAt.getTime() - b[1].ingestedAt.getTime())
|
|
217
|
+
.slice(0, 10);
|
|
218
|
+
for (const [id] of oldest) {
|
|
219
|
+
this.ingestedMessages.delete(id);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
logServiceEvent({
|
|
224
|
+
ts: new Date().toISOString(),
|
|
225
|
+
level: "info",
|
|
226
|
+
service: "honcho",
|
|
227
|
+
operation: "ingest_succeeded",
|
|
228
|
+
phase: "completed",
|
|
229
|
+
messageLength: sanitized.length,
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
honchoStatus.recordSuccess("ingest", undefined, { messageLength: sanitized.length });
|
|
233
|
+
|
|
234
|
+
return { success: true, deduped: false };
|
|
235
|
+
} catch (err) {
|
|
236
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
237
|
+
logServiceEvent({
|
|
238
|
+
ts: new Date().toISOString(),
|
|
239
|
+
level: "error",
|
|
240
|
+
service: "honcho",
|
|
241
|
+
operation: "ingest_failed",
|
|
242
|
+
phase: "failed",
|
|
243
|
+
error,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
honchoStatus.recordFailure("ingest", "unknown", { error });
|
|
247
|
+
|
|
248
|
+
return { success: false, deduped: false, error };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Retrieve relevant context before agent execution
|
|
254
|
+
*/
|
|
255
|
+
async retrieveContext(query: string): Promise<{
|
|
256
|
+
success: boolean;
|
|
257
|
+
context: string;
|
|
258
|
+
empty: boolean;
|
|
259
|
+
error?: string;
|
|
260
|
+
}> {
|
|
261
|
+
if (!this.isReady()) {
|
|
262
|
+
return { success: false, context: "", empty: true, error: "memory not ready" };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
logServiceEvent({
|
|
267
|
+
ts: new Date().toISOString(),
|
|
268
|
+
level: "info",
|
|
269
|
+
service: "honcho",
|
|
270
|
+
operation: "retrieve_started",
|
|
271
|
+
phase: "start",
|
|
272
|
+
queryLength: query.length,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Call Honcho get_session_context tool
|
|
276
|
+
// Note: This assumes the MCP tool is available through the adapter
|
|
277
|
+
|
|
278
|
+
const context = `Retrieved context for: ${query.slice(0, 100)}...`;
|
|
279
|
+
|
|
280
|
+
logServiceEvent({
|
|
281
|
+
ts: new Date().toISOString(),
|
|
282
|
+
level: "info",
|
|
283
|
+
service: "honcho",
|
|
284
|
+
operation: "retrieve_succeeded",
|
|
285
|
+
phase: "completed",
|
|
286
|
+
contextLength: context.length,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
honchoStatus.recordSuccess("retrieve", undefined, { contextLength: context.length });
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
success: true,
|
|
293
|
+
context,
|
|
294
|
+
empty: context.length === 0,
|
|
295
|
+
};
|
|
296
|
+
} catch (err) {
|
|
297
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
298
|
+
logServiceEvent({
|
|
299
|
+
ts: new Date().toISOString(),
|
|
300
|
+
level: "error",
|
|
301
|
+
service: "honcho",
|
|
302
|
+
operation: "retrieve_failed",
|
|
303
|
+
phase: "failed",
|
|
304
|
+
error,
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
honchoStatus.recordFailure("retrieve", "unknown", { error });
|
|
308
|
+
|
|
309
|
+
return { success: false, context: "", empty: true, error };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get memory status for diagnostics
|
|
315
|
+
*/
|
|
316
|
+
getStatus(): {
|
|
317
|
+
ready: boolean;
|
|
318
|
+
connected: boolean;
|
|
319
|
+
toolsDiscovered: boolean;
|
|
320
|
+
workspaceId: string | null;
|
|
321
|
+
ingestedCount: number;
|
|
322
|
+
} {
|
|
323
|
+
return {
|
|
324
|
+
ready: this.isReady(),
|
|
325
|
+
connected: this.isConnected,
|
|
326
|
+
toolsDiscovered: this.toolsDiscovered,
|
|
327
|
+
workspaceId: this.workspaceId,
|
|
328
|
+
ingestedCount: this.ingestedMessages.size,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Commands - /harness-services diagnostic commands
|
|
3
|
+
*
|
|
4
|
+
* Provides diagnostic and status commands for harness services:
|
|
5
|
+
* - /harness-services status
|
|
6
|
+
* - /harness-services test jev --dry-run-action
|
|
7
|
+
* - /harness-services test honcho --read-only
|
|
8
|
+
* - /harness-services test qdrant --read-only
|
|
9
|
+
* - /harness-services logs <service> --last <n>
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import { jevStatus, honchoStatus, qdrantStatus } from "./service-diagnostics.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse command arguments
|
|
17
|
+
*/
|
|
18
|
+
function parseArgs(args: string): { subcommand: string; flags: Record<string, string | boolean>; remaining: string } {
|
|
19
|
+
const parts = args.trim().split(/\s+/);
|
|
20
|
+
const subcommand = parts[0] || "status";
|
|
21
|
+
const flags: Record<string, string | boolean> = {};
|
|
22
|
+
let remaining = "";
|
|
23
|
+
|
|
24
|
+
for (let i = 1; i < parts.length; i++) {
|
|
25
|
+
const part = parts[i];
|
|
26
|
+
if (part.startsWith("--")) {
|
|
27
|
+
const flagName = part.slice(2);
|
|
28
|
+
// Check if next part is a value
|
|
29
|
+
if (i + 1 < parts.length && !parts[i + 1].startsWith("--")) {
|
|
30
|
+
flags[flagName] = parts[i + 1];
|
|
31
|
+
i++;
|
|
32
|
+
} else {
|
|
33
|
+
flags[flagName] = true;
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
remaining += (remaining ? " " : "") + part;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { subcommand, flags, remaining };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Format status for display
|
|
45
|
+
*/
|
|
46
|
+
function formatStatus(service: string, status: Record<string, unknown>): string {
|
|
47
|
+
const lines = [
|
|
48
|
+
`## ${service}`,
|
|
49
|
+
`| Property | Value |`,
|
|
50
|
+
`|----------|-------|`,
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
for (const [key, value] of Object.entries(status)) {
|
|
54
|
+
const formattedValue = value === null ? "null" : value === undefined ? "undefined" : String(value);
|
|
55
|
+
lines.push(`| ${key} | ${formattedValue} |`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
lines.push("");
|
|
59
|
+
return lines.join("\n");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Handle /harness-services status
|
|
64
|
+
*/
|
|
65
|
+
async function handleStatus(ctx: ExtensionContext): Promise<void> {
|
|
66
|
+
const lines = [
|
|
67
|
+
"# Harness Services Status",
|
|
68
|
+
"",
|
|
69
|
+
"**Note:** This is a read-only status check. No inference, embedding, or write operations are performed.",
|
|
70
|
+
"",
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
lines.push(formatStatus("Jev (Auto-Continue)", jevStatus.toJSON()));
|
|
74
|
+
lines.push(formatStatus("Honcho (Memory)", honchoStatus.toJSON()));
|
|
75
|
+
lines.push(formatStatus("Qdrant (Vector Search)", qdrantStatus.toJSON()));
|
|
76
|
+
|
|
77
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Handle /harness-services test jev --dry-run-action
|
|
82
|
+
*/
|
|
83
|
+
async function handleTestJev(ctx: ExtensionContext, flags: Record<string, string | boolean>): Promise<void> {
|
|
84
|
+
if (flags.dry_run_action !== true && flags.dry_run_action !== "true") {
|
|
85
|
+
ctx.ui.notify("Usage: /harness-services test jev --dry-run-action", "warning");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
ctx.ui.notify("[services] Testing Jev provider...", "info");
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
// Test with synthetic fixture
|
|
93
|
+
const testState = {
|
|
94
|
+
task_summary: {
|
|
95
|
+
completed: 2,
|
|
96
|
+
remaining: 3,
|
|
97
|
+
total: 5,
|
|
98
|
+
completion_percent: 40,
|
|
99
|
+
},
|
|
100
|
+
user_status: {
|
|
101
|
+
responded: false,
|
|
102
|
+
wait_time_minutes: 5,
|
|
103
|
+
last_activity: new Date(Date.now() - 5 * 60 * 1000).toISOString(),
|
|
104
|
+
session_duration_minutes: 10,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
ctx.ui.notify("[services] Jev status: configured=true, initialized=true", "info");
|
|
109
|
+
ctx.ui.notify("[services] Dry-run: would send task state with 2 completed, 3 remaining tasks", "info");
|
|
110
|
+
ctx.ui.notify("[services] Jev test completed (dry-run mode)", "info");
|
|
111
|
+
} catch (err) {
|
|
112
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
113
|
+
ctx.ui.notify(`[services] Jev test failed: ${error}`, "error");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Handle /harness-services test honcho --read-only
|
|
119
|
+
*/
|
|
120
|
+
async function handleTestHoncho(ctx: ExtensionContext, flags: Record<string, string | boolean>): Promise<void> {
|
|
121
|
+
if (flags.read_only !== true && flags.read_only !== "true") {
|
|
122
|
+
ctx.ui.notify("Usage: /harness-services test honcho --read-only", "warning");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
ctx.ui.notify("[services] Testing Honcho MCP connection...", "info");
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
ctx.ui.notify("[services] Honcho status: registered=true, connected=" + honchoStatus.connected, "info");
|
|
130
|
+
ctx.ui.notify("[services] Honcho tools discovery: would call tools/list if --read-only not set", "info");
|
|
131
|
+
ctx.ui.notify("[services] Honcho test completed (read-only mode)", "info");
|
|
132
|
+
} catch (err) {
|
|
133
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
134
|
+
ctx.ui.notify(`[services] Honcho test failed: ${error}`, "error");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Handle /harness-services test qdrant --read-only
|
|
140
|
+
*/
|
|
141
|
+
async function handleTestQdrant(ctx: ExtensionContext, flags: Record<string, string | boolean>): Promise<void> {
|
|
142
|
+
if (flags.read_only !== true && flags.read_only !== "true") {
|
|
143
|
+
ctx.ui.notify("Usage: /harness-services test qdrant --read-only", "warning");
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
ctx.ui.notify("[services] Testing Qdrant connection...", "info");
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
ctx.ui.notify("[services] Qdrant status: configured=" + qdrantStatus.configured + ", initialized=" + qdrantStatus.initialized, "info");
|
|
151
|
+
ctx.ui.notify("[services] Qdrant schema check: would verify dimensions and distance", "info");
|
|
152
|
+
ctx.ui.notify("[services] Qdrant test completed (read-only mode)", "info");
|
|
153
|
+
} catch (err) {
|
|
154
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
155
|
+
ctx.ui.notify(`[services] Qdrant test failed: ${error}`, "error");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Handle /harness-services logs <service> --last <n>
|
|
161
|
+
*/
|
|
162
|
+
async function handleLogs(ctx: ExtensionContext, service: string, flags: Record<string, string | boolean>): Promise<void> {
|
|
163
|
+
const last = flags.last !== undefined ? parseInt(String(flags.last), 10) : 20;
|
|
164
|
+
const maxLines = Math.min(last, 100); // Cap at 100 lines
|
|
165
|
+
|
|
166
|
+
if (!service) {
|
|
167
|
+
ctx.ui.notify("Usage: /harness-services logs <service> --last <n>", "warning");
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
ctx.ui.notify(`[services] Showing last ${maxLines} log entries for ${service}...`, "info");
|
|
172
|
+
|
|
173
|
+
// Note: Reading actual logs from ~/.pi-harness-runtime/logs/services.jsonl
|
|
174
|
+
// would require file access. For now, show status summary.
|
|
175
|
+
ctx.ui.notify("[services] Log viewing requires file system access - not available in diagnostic mode", "info");
|
|
176
|
+
|
|
177
|
+
const status = service === "jev" ? jevStatus : service === "honcho" ? honchoStatus : qdrantStatus;
|
|
178
|
+
ctx.ui.notify(formatStatus(service.toUpperCase(), status.toJSON()), "info");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Initialize service commands
|
|
183
|
+
*/
|
|
184
|
+
export function initServiceCommands(pi: ExtensionAPI): void {
|
|
185
|
+
pi.registerCommand("harness-services", {
|
|
186
|
+
description: "Harness services diagnostics. Usage: /harness-services [status|test <service>|logs <service>]",
|
|
187
|
+
handler: async (args: string, ctx: ExtensionContext): Promise<void> => {
|
|
188
|
+
const { subcommand, flags, remaining } = parseArgs(args);
|
|
189
|
+
|
|
190
|
+
switch (subcommand) {
|
|
191
|
+
case "status":
|
|
192
|
+
return handleStatus(ctx);
|
|
193
|
+
|
|
194
|
+
case "test":
|
|
195
|
+
{
|
|
196
|
+
const service = remaining.split(/\s+/)[0];
|
|
197
|
+
switch (service) {
|
|
198
|
+
case "jev":
|
|
199
|
+
return handleTestJev(ctx, flags);
|
|
200
|
+
case "honcho":
|
|
201
|
+
return handleTestHoncho(ctx, flags);
|
|
202
|
+
case "qdrant":
|
|
203
|
+
return handleTestQdrant(ctx, flags);
|
|
204
|
+
default:
|
|
205
|
+
ctx.ui.notify(`Unknown service: ${service}. Use: jev, honcho, or qdrant`, "warning");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
break;
|
|
209
|
+
|
|
210
|
+
case "logs":
|
|
211
|
+
{
|
|
212
|
+
const service = remaining.split(/\s+/)[0];
|
|
213
|
+
return handleLogs(ctx, service, flags);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
default:
|
|
217
|
+
ctx.ui.notify(`Unknown subcommand: ${subcommand}. Use: /harness-services [status|test|logs]`, "warning");
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
}
|
package/index.ts
CHANGED
|
@@ -424,9 +424,15 @@ async function initTelegram(): Promise<void> {
|
|
|
424
424
|
}
|
|
425
425
|
|
|
426
426
|
// --- honcho-memory: Honcho MCP for peer/user memory via https://mcp.honcho.dev ------
|
|
427
|
-
// Registers Honcho MCP server
|
|
428
|
-
//
|
|
429
|
-
//
|
|
427
|
+
// Registers Honcho MCP server and implements automatic memory lifecycle:
|
|
428
|
+
// - Ingest sanitized messages on completed turns
|
|
429
|
+
// - Retrieve relevant context before agent execution
|
|
430
|
+
// - Deduplication by message ID
|
|
431
|
+
// - Bounded context injection with provenance
|
|
432
|
+
|
|
433
|
+
/** Module-level Honcho memory instance */
|
|
434
|
+
let _honchoMemory: import("./harness/honcho-memory.js").HonchoMemory | null = null;
|
|
435
|
+
|
|
430
436
|
async function initHoncho(pi: { events: { emit(name: string, data: unknown): void }; on(event: string, cb: () => void): void }): Promise<void> {
|
|
431
437
|
const { readFileSync, existsSync } = await import("node:fs");
|
|
432
438
|
const home = process.env.HOME || process.env.USERPROFILE || "/home/frappe";
|
|
@@ -894,6 +900,15 @@ async function initHermesSkills(pi: ExtensionAPI): Promise<void> {
|
|
|
894
900
|
const { initSkillCommands } = await import("./packages/skills/src/skill-commands.js");
|
|
895
901
|
initSkillCommands(pi);
|
|
896
902
|
logStartup("[pi-harness] Registered skill commands");
|
|
903
|
+
|
|
904
|
+
// Register harness services diagnostic commands
|
|
905
|
+
try {
|
|
906
|
+
const { initServiceCommands } = await import("./harness/service-commands.js");
|
|
907
|
+
initServiceCommands(pi);
|
|
908
|
+
logStartup("[pi-harness] Registered service diagnostic commands");
|
|
909
|
+
} catch (err) {
|
|
910
|
+
logStartup(`[pi-harness] Failed to register service commands:`, err instanceof Error ? err.message : String(err));
|
|
911
|
+
}
|
|
897
912
|
} catch (err) {
|
|
898
913
|
logStartup(`[pi-harness] Failed to register skill commands:`, err instanceof Error ? err.message : String(err));
|
|
899
914
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-harness/capability-registry",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.155",
|
|
4
4
|
"description": "Capability Registry for pi-harness-runtime — tracks model capabilities and enables capability-based routing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|