pi-harness-runtime 1.1.144 → 1.1.145
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/index.ts +167 -10
- package/package.json +1 -1
- package/packages/a2a-adapter/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/dist/adapters/telegram-adapter.d.ts.map +1 -1
- package/packages/notification/dist/adapters/telegram-adapter.js.map +1 -1
- package/packages/notification/dist/notification-center.d.ts.map +1 -1
- package/packages/notification/dist/notification-center.js +12 -0
- package/packages/notification/dist/notification-center.js.map +1 -1
- package/packages/notification/dist/types.d.ts +1 -1
- package/packages/notification/dist/types.d.ts.map +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/notification/src/adapters/telegram-adapter.ts +2 -0
- package/packages/notification/src/notification-center.ts +12 -0
- package/packages/notification/src/types.ts +3 -0
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/dist/index.d.ts +121 -0
- package/packages/paperclip-integration/dist/index.d.ts.map +1 -0
- package/packages/paperclip-integration/dist/index.js +228 -0
- package/packages/paperclip-integration/dist/index.js.map +1 -0
- package/packages/paperclip-integration/package.json +20 -0
- package/packages/paperclip-integration/src/index.ts +326 -0
- 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/package.json +1 -1
- 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/package.json +1 -1
- 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,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pi-harness/paperclip-integration",
|
|
3
|
+
"version": "1.1.145",
|
|
4
|
+
"description": "Paperclip API integration: poll agent inbox, route tasks to pi.dev, Telegram alerts",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc --skipLibCheck"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "^5.7.3"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paperclip Integration — RFC-00XX
|
|
3
|
+
*
|
|
4
|
+
* Connects pi-harness-runtime to Paperclip's control plane.
|
|
5
|
+
*
|
|
6
|
+
* Architecture:
|
|
7
|
+
* Paperclip (control plane) → REST API → pi-harness inbox watcher
|
|
8
|
+
* → Telegram alerts → pi.dev agent executes → Paperclip task updates
|
|
9
|
+
*
|
|
10
|
+
* Key flows:
|
|
11
|
+
* 1. Poll Paperclip inbox (assigned tasks) → route to pi.dev via sendUserMessage
|
|
12
|
+
* 2. Emit Telegram alerts for all task state changes
|
|
13
|
+
* 3. Update Paperclip issue status on task completion/failure
|
|
14
|
+
*
|
|
15
|
+
* Paperclip API base: https://api.paperclip.inc/api
|
|
16
|
+
* Auth: Bearer token (agent API key)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Types
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
export interface PaperclipConfig {
|
|
24
|
+
/** Paperclip API base URL */
|
|
25
|
+
baseUrl: string;
|
|
26
|
+
/** Agent API key (bearer token) */
|
|
27
|
+
apiKey: string;
|
|
28
|
+
/** Company ID for company-scoped endpoints */
|
|
29
|
+
companyId?: string;
|
|
30
|
+
/** Poll interval in ms (default: 30000 = 30s) */
|
|
31
|
+
pollIntervalMs?: number;
|
|
32
|
+
/** Called with a Telegram-friendly summary when a task is detected */
|
|
33
|
+
onTaskDetected?: (task: PaperclipIssue) => void;
|
|
34
|
+
/** Called when task status changes */
|
|
35
|
+
onTaskStatusChange?: (task: PaperclipIssue, oldStatus: string) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface PaperclipIssue {
|
|
39
|
+
id: string;
|
|
40
|
+
identifier: string; // e.g. "PAP-39"
|
|
41
|
+
title: string;
|
|
42
|
+
status: string;
|
|
43
|
+
priority?: string;
|
|
44
|
+
assigneeId?: string;
|
|
45
|
+
assigneeName?: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
createdAt: string;
|
|
48
|
+
updatedAt: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface PaperclipAgentMe {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
email: string;
|
|
55
|
+
companyId: string;
|
|
56
|
+
companyName: string;
|
|
57
|
+
role: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Paperclip Client
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
export class PaperclipClient {
|
|
65
|
+
private readonly baseUrl: string;
|
|
66
|
+
private readonly apiKey: string;
|
|
67
|
+
private readonly headers: Record<string, string>;
|
|
68
|
+
|
|
69
|
+
constructor(config: PaperclipConfig) {
|
|
70
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
71
|
+
this.apiKey = config.apiKey;
|
|
72
|
+
this.headers = {
|
|
73
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
74
|
+
"Content-Type": "application/json",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get current agent's identity and company context
|
|
80
|
+
*/
|
|
81
|
+
async getAgentMe(): Promise<PaperclipAgentMe> {
|
|
82
|
+
const res = await this.request<PaperclipAgentMe>("GET", "/api/agents/me");
|
|
83
|
+
return res;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get inbox issues assigned to this agent (mine-tab equivalent)
|
|
88
|
+
*/
|
|
89
|
+
async getInbox(companyId: string): Promise<PaperclipIssue[]> {
|
|
90
|
+
const params = new URLSearchParams({
|
|
91
|
+
status: "todo,in_progress,needs_review",
|
|
92
|
+
assigneeId: "me",
|
|
93
|
+
});
|
|
94
|
+
const res = await this.request<{ issues: PaperclipIssue[] }>(
|
|
95
|
+
"GET",
|
|
96
|
+
`/api/companies/${companyId}/issues?${params}`,
|
|
97
|
+
);
|
|
98
|
+
return res.issues ?? [];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get a single issue by ID or human-readable identifier
|
|
103
|
+
*/
|
|
104
|
+
async getIssue(issueIdOrIdentifier: string): Promise<PaperclipIssue> {
|
|
105
|
+
// Try as identifier first (PAP-39), then as UUID
|
|
106
|
+
try {
|
|
107
|
+
return await this.request<PaperclipIssue>("GET", `/api/issues/${issueIdOrIdentifier}`);
|
|
108
|
+
} catch {
|
|
109
|
+
return await this.request<PaperclipIssue>(
|
|
110
|
+
"GET",
|
|
111
|
+
`/api/issues/by-identifier/${issueIdOrIdentifier}`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Update issue status
|
|
118
|
+
*/
|
|
119
|
+
async updateIssueStatus(
|
|
120
|
+
issueIdOrIdentifier: string,
|
|
121
|
+
status: string,
|
|
122
|
+
): Promise<PaperclipIssue> {
|
|
123
|
+
return this.request<PaperclipIssue>("PATCH", `/api/issues/${issueIdOrIdentifier}`, { body: JSON.stringify({ status }) });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Add a comment to an issue
|
|
128
|
+
*/
|
|
129
|
+
async addComment(issueId: string, body: string): Promise<void> {
|
|
130
|
+
await this.request("POST", `/api/issues/${issueId}/comments`, { body: JSON.stringify({ body }) });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private async request<T>(
|
|
134
|
+
method: string,
|
|
135
|
+
path: string,
|
|
136
|
+
options: { body?: string } = {},
|
|
137
|
+
): Promise<T> {
|
|
138
|
+
const url = path.startsWith("http")
|
|
139
|
+
? path
|
|
140
|
+
: `${this.baseUrl}${path}`;
|
|
141
|
+
|
|
142
|
+
const res = await fetch(url, {
|
|
143
|
+
method,
|
|
144
|
+
headers: this.headers,
|
|
145
|
+
body: options.body,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (!res.ok) {
|
|
149
|
+
const text = await res.text().catch(() => "");
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Paperclip API ${method} ${path} failed (${res.status}): ${text}`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return res.json() as Promise<T>;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
// Paperclip Watcher — polls inbox, routes tasks to pi.dev
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
export interface PaperclipWatcherOptions {
|
|
164
|
+
/** pi-harness Telegram nc() helper */
|
|
165
|
+
nc: (event: string, ctx: Record<string, unknown>) => Promise<void>;
|
|
166
|
+
/** pi.dev sendUserMessage function */
|
|
167
|
+
sendUserMessage: (content: string, opts?: { deliverAs?: string }) => void;
|
|
168
|
+
/** Optional: update Paperclip issue status after routing */
|
|
169
|
+
onRouted?: (issue: PaperclipIssue) => Promise<void>;
|
|
170
|
+
/** Poll interval in ms (default: 30s) */
|
|
171
|
+
pollIntervalMs?: number;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export class PaperclipWatcher {
|
|
175
|
+
private client: PaperclipClient;
|
|
176
|
+
private options: PaperclipWatcherOptions;
|
|
177
|
+
private timer: ReturnType<typeof setInterval> | null = null;
|
|
178
|
+
private seenIds = new Set<string>();
|
|
179
|
+
private companyId: string | null = null;
|
|
180
|
+
private agentName: string = "agent";
|
|
181
|
+
|
|
182
|
+
constructor(config: PaperclipConfig, options: PaperclipWatcherOptions) {
|
|
183
|
+
this.client = new PaperclipClient(config);
|
|
184
|
+
this.options = options;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Start polling the Paperclip inbox
|
|
189
|
+
*/
|
|
190
|
+
async start(): Promise<void> {
|
|
191
|
+
// Fetch agent identity
|
|
192
|
+
try {
|
|
193
|
+
const me = await this.client.getAgentMe();
|
|
194
|
+
this.companyId = me.companyId;
|
|
195
|
+
this.agentName = me.name;
|
|
196
|
+
console.error(`[paperclip] Agent: ${me.name} @ ${me.companyName}`);
|
|
197
|
+
} catch (err) {
|
|
198
|
+
console.error(
|
|
199
|
+
`[paperclip] Failed to get agent identity: ${err instanceof Error ? err.message : String(err)}`,
|
|
200
|
+
);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Initial poll
|
|
205
|
+
await this.poll();
|
|
206
|
+
|
|
207
|
+
// Schedule polling
|
|
208
|
+
const interval = this.options.pollIntervalMs ?? 30_000;
|
|
209
|
+
this.timer = setInterval(() => {
|
|
210
|
+
void this.poll();
|
|
211
|
+
}, interval);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Stop polling
|
|
216
|
+
*/
|
|
217
|
+
stop(): void {
|
|
218
|
+
if (this.timer) {
|
|
219
|
+
clearInterval(this.timer);
|
|
220
|
+
this.timer = null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Manually trigger a poll (useful after Telegram command)
|
|
226
|
+
*/
|
|
227
|
+
async pollNow(): Promise<void> {
|
|
228
|
+
await this.poll();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private async poll(): Promise<void> {
|
|
232
|
+
if (!this.companyId) return;
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
const issues = await this.client.getInbox(this.companyId);
|
|
236
|
+
|
|
237
|
+
for (const issue of issues) {
|
|
238
|
+
// Skip already-seen issues
|
|
239
|
+
if (this.seenIds.has(issue.id)) continue;
|
|
240
|
+
this.seenIds.add(issue.id);
|
|
241
|
+
|
|
242
|
+
// Route to Telegram
|
|
243
|
+
this.options.nc("TaskCompleted", {
|
|
244
|
+
requirement: `[Paperclip] ${issue.identifier}: ${issue.title}`,
|
|
245
|
+
taskTitle: issue.title,
|
|
246
|
+
taskId: issue.id,
|
|
247
|
+
error: `New task assigned: ${issue.priority ? `P${issue.priority}` : ""}`,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Route to pi.dev
|
|
251
|
+
const body = `[Paperclip task assigned to ${this.agentName}]\n\n` +
|
|
252
|
+
`**${issue.identifier}**: ${issue.title}\n` +
|
|
253
|
+
`Status: ${issue.status}${issue.priority ? `\nPriority: ${issue.priority}` : ""}\n` +
|
|
254
|
+
`\n${issue.description ?? ""}`;
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
this.options.sendUserMessage(body, { deliverAs: "steer" });
|
|
258
|
+
} catch {
|
|
259
|
+
// pi.dev may not be ready
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Mark as in_progress in Paperclip
|
|
263
|
+
this.options.onRouted?.(issue);
|
|
264
|
+
}
|
|
265
|
+
} catch (err) {
|
|
266
|
+
console.error(
|
|
267
|
+
`[paperclip] Poll failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
// Auto-init from env / keys file
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Check if Paperclip is configured
|
|
279
|
+
*/
|
|
280
|
+
export function hasPaperclipConfig(): boolean {
|
|
281
|
+
const apiKey =
|
|
282
|
+
process.env.PAPERCLIP_API_KEY ||
|
|
283
|
+
process.env.PAPERCLIP_API_KEY;
|
|
284
|
+
if (!apiKey) {
|
|
285
|
+
const homedir = process.env.HOME || process.env.USERPROFILE || "/home/frappe";
|
|
286
|
+
try {
|
|
287
|
+
const { readFileSync, existsSync } = require("fs");
|
|
288
|
+
const path = `${homedir}/.pi-harness-runtime/keys/paperclip-api-key.txt`;
|
|
289
|
+
if (existsSync(path)) {
|
|
290
|
+
const key = readFileSync(path, "utf8").trim();
|
|
291
|
+
return key.length > 10;
|
|
292
|
+
}
|
|
293
|
+
} catch {
|
|
294
|
+
// ignore
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return !!apiKey;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Get Paperclip config from environment / keys file
|
|
302
|
+
*/
|
|
303
|
+
export function getPaperclipConfig(): PaperclipConfig | null {
|
|
304
|
+
const apiKey =
|
|
305
|
+
process.env.PAPERCLIP_API_KEY ||
|
|
306
|
+
process.env.PAPERCLIP_API_KEY;
|
|
307
|
+
const baseUrl =
|
|
308
|
+
process.env.PAPERCLIP_BASE_URL ||
|
|
309
|
+
"https://api.paperclip.inc/api";
|
|
310
|
+
|
|
311
|
+
if (!apiKey) {
|
|
312
|
+
const homedir = process.env.HOME || process.env.USERPROFILE || "/home/frappe";
|
|
313
|
+
try {
|
|
314
|
+
const { readFileSync, existsSync } = require("fs");
|
|
315
|
+
const path = `${homedir}/.pi-harness-runtime/keys/paperclip-api-key.txt`;
|
|
316
|
+
if (!existsSync(path)) return null;
|
|
317
|
+
const key = readFileSync(path, "utf8").trim();
|
|
318
|
+
if (!key || key.length < 10) return null;
|
|
319
|
+
return { apiKey: key, baseUrl };
|
|
320
|
+
} catch {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return { apiKey, baseUrl };
|
|
326
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-harness/provider-router",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.145",
|
|
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",
|