@zetaloop/chappie 0.3.1 → 0.4.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.
- package/README.md +11 -3
- package/docs/tools.md +50 -4
- package/package.json +1 -1
- package/src/activity.ts +21 -0
- package/src/broker.ts +476 -113
- package/src/config.ts +1 -1
- package/src/delivery.ts +3 -2
- package/src/history.ts +123 -0
- package/src/instructions.md +3 -1
- package/src/ipc.ts +14 -1
- package/src/provider.ts +2 -1
- package/src/server.ts +154 -31
- package/src/session.ts +49 -8
- package/src/state.ts +12 -17
- package/src/tools.ts +12 -2
package/src/session.ts
CHANGED
|
@@ -13,7 +13,9 @@ import type {
|
|
|
13
13
|
SessionEntry,
|
|
14
14
|
} from "@earendil-works/pi-coding-agent";
|
|
15
15
|
import { Text } from "@earendil-works/pi-tui";
|
|
16
|
+
import { type Activity, chatLabel, source } from "./activity.ts";
|
|
16
17
|
import type { DeliveryRecord } from "./delivery.ts";
|
|
18
|
+
import { historyResult } from "./history.ts";
|
|
17
19
|
import {
|
|
18
20
|
type BrokerMessage,
|
|
19
21
|
IpcClient,
|
|
@@ -27,7 +29,7 @@ import { readSessionResource, rememberImages } from "./resources.ts";
|
|
|
27
29
|
|
|
28
30
|
type RemoteRequest = Extract<BrokerMessage, { type: "chat" | "call" }>;
|
|
29
31
|
|
|
30
|
-
interface Notice {
|
|
32
|
+
interface Notice extends Activity {
|
|
31
33
|
message: string;
|
|
32
34
|
type: "info" | "warning" | "error";
|
|
33
35
|
}
|
|
@@ -126,9 +128,17 @@ export class LocalSession {
|
|
|
126
128
|
this.#pi.on("session_shutdown", () => this.close());
|
|
127
129
|
}
|
|
128
130
|
|
|
129
|
-
#notify(
|
|
131
|
+
#notify(
|
|
132
|
+
message: string,
|
|
133
|
+
type: Notice["type"] = "info",
|
|
134
|
+
activity: Activity = {},
|
|
135
|
+
): void {
|
|
130
136
|
if (this.#context)
|
|
131
|
-
this.#pi.appendEntry<Notice>("chappie.notice", {
|
|
137
|
+
this.#pi.appendEntry<Notice>("chappie.notice", {
|
|
138
|
+
message,
|
|
139
|
+
type,
|
|
140
|
+
...activity,
|
|
141
|
+
});
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
async start(output: ProviderOutput): Promise<void> {
|
|
@@ -194,7 +204,6 @@ export class LocalSession {
|
|
|
194
204
|
this.#resetInputs(context);
|
|
195
205
|
}
|
|
196
206
|
if (!this.#connection) {
|
|
197
|
-
this.#notify("Connecting to Chappie…");
|
|
198
207
|
this.#connection = new IpcClient(this.#agentDir, this.#connect, {
|
|
199
208
|
onOpen: async () => {
|
|
200
209
|
await this.#sync();
|
|
@@ -262,7 +271,7 @@ export class LocalSession {
|
|
|
262
271
|
if (
|
|
263
272
|
message.sessionId === this.#context?.sessionManager.getSessionId()
|
|
264
273
|
) {
|
|
265
|
-
this.#notify(message.message);
|
|
274
|
+
this.#notify(message.message, "info", message.activity);
|
|
266
275
|
}
|
|
267
276
|
break;
|
|
268
277
|
case "inspect":
|
|
@@ -277,6 +286,26 @@ export class LocalSession {
|
|
|
277
286
|
};
|
|
278
287
|
});
|
|
279
288
|
break;
|
|
289
|
+
case "history":
|
|
290
|
+
await this.#reply(message.id, message.sessionId, () => {
|
|
291
|
+
const context = this.#context;
|
|
292
|
+
if (!context) throw new Error("Chappie session is not available");
|
|
293
|
+
const history = historyResult(
|
|
294
|
+
context.sessionManager.getBranch(),
|
|
295
|
+
message.sessionId,
|
|
296
|
+
message.range,
|
|
297
|
+
);
|
|
298
|
+
this.#notify(
|
|
299
|
+
`${chatLabel(message)} read history: ${history.count} entries`,
|
|
300
|
+
"info",
|
|
301
|
+
{
|
|
302
|
+
event: "history",
|
|
303
|
+
...source(message.chatId, message.requestId),
|
|
304
|
+
},
|
|
305
|
+
);
|
|
306
|
+
return { type: "result", id: message.id, cwd: context.cwd, history };
|
|
307
|
+
});
|
|
308
|
+
break;
|
|
280
309
|
case "ackInputs":
|
|
281
310
|
if (
|
|
282
311
|
message.sessionId === this.#context?.sessionManager.getSessionId()
|
|
@@ -306,8 +335,12 @@ export class LocalSession {
|
|
|
306
335
|
? [...new Set(request.calls.map((call) => call.name))].join(", ")
|
|
307
336
|
: "chat";
|
|
308
337
|
this.#notify(
|
|
309
|
-
`${name} cancelled for
|
|
338
|
+
`${name} cancelled for ${chatLabel(request)}: ${message.reason}`,
|
|
310
339
|
"warning",
|
|
340
|
+
{
|
|
341
|
+
event: "cancelled",
|
|
342
|
+
...source(request.chatId, request.requestId),
|
|
343
|
+
},
|
|
311
344
|
);
|
|
312
345
|
if (queued !== -1) {
|
|
313
346
|
this.#queue.splice(queued, 1);
|
|
@@ -371,6 +404,7 @@ export class LocalSession {
|
|
|
371
404
|
}
|
|
372
405
|
|
|
373
406
|
this.#queue.shift();
|
|
407
|
+
output.message.chappie = source(request.chatId, request.requestId);
|
|
374
408
|
this.#active = {
|
|
375
409
|
request,
|
|
376
410
|
session: this.#description(),
|
|
@@ -432,7 +466,7 @@ export class LocalSession {
|
|
|
432
466
|
if (active.cancelled !== undefined) {
|
|
433
467
|
const delivery: DeliveryRecord = {
|
|
434
468
|
id: randomUUID(),
|
|
435
|
-
chatId
|
|
469
|
+
...source(active.request.chatId, active.request.requestId),
|
|
436
470
|
sessionId: active.session.id,
|
|
437
471
|
cwd: active.session.cwd,
|
|
438
472
|
toolResults: active.toolResults,
|
|
@@ -516,7 +550,14 @@ export class LocalSession {
|
|
|
516
550
|
await connection.send({ type: "delivery", delivery });
|
|
517
551
|
await completion.promise;
|
|
518
552
|
this.#deliveries.delete(delivery.id);
|
|
519
|
-
this.#notify(
|
|
553
|
+
this.#notify(
|
|
554
|
+
`Result saved for ChatGPT ${delivery.chatId.slice(-4)}`,
|
|
555
|
+
"info",
|
|
556
|
+
{
|
|
557
|
+
event: "result_saved",
|
|
558
|
+
...source(delivery.chatId, delivery.requestId),
|
|
559
|
+
},
|
|
560
|
+
);
|
|
520
561
|
} finally {
|
|
521
562
|
this.#stores.delete(delivery.id);
|
|
522
563
|
}
|
package/src/state.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { QuestionAnswer, QuestionRecord } from "./questions.ts";
|
|
|
5
5
|
|
|
6
6
|
interface StateFile {
|
|
7
7
|
bindings?: Record<string, string>;
|
|
8
|
-
|
|
8
|
+
codes?: Record<string, string>;
|
|
9
9
|
deliveries?: DeliveryRecord[];
|
|
10
10
|
questions?: QuestionRecord[];
|
|
11
11
|
}
|
|
@@ -14,7 +14,7 @@ export class State {
|
|
|
14
14
|
readonly #path: string;
|
|
15
15
|
readonly #temporaryPath: string;
|
|
16
16
|
readonly #bindings = new Map<string, string>();
|
|
17
|
-
readonly #
|
|
17
|
+
readonly #codes = new Map<string, string>();
|
|
18
18
|
readonly #deliveries = new Map<string, DeliveryRecord>();
|
|
19
19
|
readonly #questions = new Map<string, QuestionRecord>();
|
|
20
20
|
#writes = Promise.resolve();
|
|
@@ -36,8 +36,8 @@ export class State {
|
|
|
36
36
|
for (const [chatId, sessionId] of Object.entries(state.bindings ?? {})) {
|
|
37
37
|
if (typeof sessionId === "string") this.#bindings.set(chatId, sessionId);
|
|
38
38
|
}
|
|
39
|
-
for (const [
|
|
40
|
-
this.#
|
|
39
|
+
for (const [sessionId, code] of Object.entries(state.codes ?? {})) {
|
|
40
|
+
this.#codes.set(sessionId, code);
|
|
41
41
|
}
|
|
42
42
|
for (const delivery of state.deliveries ?? []) {
|
|
43
43
|
if (delivery?.id) this.#deliveries.set(delivery.id, delivery);
|
|
@@ -46,12 +46,13 @@ export class State {
|
|
|
46
46
|
this.#questions.set(question.id, question);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
return this.#
|
|
49
|
+
code(sessionId: string): string | undefined {
|
|
50
|
+
return this.#codes.get(sessionId);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
this.#
|
|
53
|
+
setCode(sessionId: string, code: string | undefined): Promise<void> {
|
|
54
|
+
if (code === undefined) this.#codes.delete(sessionId);
|
|
55
|
+
else this.#codes.set(sessionId, code);
|
|
55
56
|
return this.#save();
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -59,12 +60,6 @@ export class State {
|
|
|
59
60
|
return this.#bindings.get(chatId);
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
chats(sessionId: string): string[] {
|
|
63
|
-
return [...this.#bindings]
|
|
64
|
-
.filter(([, target]) => target === sessionId)
|
|
65
|
-
.map(([chatId]) => chatId);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
63
|
bindingCounts(): Map<string, number> {
|
|
69
64
|
const counts = new Map<string, number>();
|
|
70
65
|
for (const sessionId of this.#bindings.values()) {
|
|
@@ -73,7 +68,7 @@ export class State {
|
|
|
73
68
|
return counts;
|
|
74
69
|
}
|
|
75
70
|
|
|
76
|
-
|
|
71
|
+
bind(chatId: string, sessionId: string): Promise<void> {
|
|
77
72
|
this.#bindings.set(chatId, sessionId);
|
|
78
73
|
return this.#save();
|
|
79
74
|
}
|
|
@@ -168,10 +163,10 @@ export class State {
|
|
|
168
163
|
#save(): Promise<void> {
|
|
169
164
|
const saved = this.#writes.then(async () => {
|
|
170
165
|
const bindings = Object.fromEntries(this.#bindings);
|
|
171
|
-
const
|
|
166
|
+
const codes = Object.fromEntries(this.#codes);
|
|
172
167
|
await writeFile(
|
|
173
168
|
this.#temporaryPath,
|
|
174
|
-
`${JSON.stringify({ bindings,
|
|
169
|
+
`${JSON.stringify({ bindings, codes, deliveries: [...this.#deliveries.values()], questions: [...this.#questions.values()] }, null, 2)}\n`,
|
|
175
170
|
{
|
|
176
171
|
mode: 0o600,
|
|
177
172
|
},
|
package/src/tools.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
createWriteToolDefinition,
|
|
7
7
|
} from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { fromJsonSchema } from "@modelcontextprotocol/server";
|
|
9
|
+
import type { Initialization } from "./broker.ts";
|
|
9
10
|
import { toolResultsContent } from "./delivery.ts";
|
|
10
11
|
import type { SessionInput } from "./ipc.ts";
|
|
11
12
|
import { contentWithImageReferences } from "./resources.ts";
|
|
@@ -38,10 +39,18 @@ export function toolResult(
|
|
|
38
39
|
sessionId: string,
|
|
39
40
|
cwd: string,
|
|
40
41
|
inputs: SessionInput[] = [],
|
|
42
|
+
initialization?: Initialization,
|
|
41
43
|
) {
|
|
42
44
|
return {
|
|
43
45
|
content: [
|
|
44
|
-
{
|
|
46
|
+
{
|
|
47
|
+
type: "text" as const,
|
|
48
|
+
text: JSON.stringify({
|
|
49
|
+
sessionId,
|
|
50
|
+
cwd,
|
|
51
|
+
...(initialization ? { initialization } : {}),
|
|
52
|
+
}),
|
|
53
|
+
},
|
|
45
54
|
...toolResultsContent(toolResults, sessionId),
|
|
46
55
|
...inputContent(inputs),
|
|
47
56
|
],
|
|
@@ -73,7 +82,8 @@ function withSessionId(
|
|
|
73
82
|
...copy.properties,
|
|
74
83
|
sessionId: {
|
|
75
84
|
type: "string",
|
|
76
|
-
description:
|
|
85
|
+
description:
|
|
86
|
+
"Pi session for this operation; becomes the default if none is set",
|
|
77
87
|
},
|
|
78
88
|
},
|
|
79
89
|
additionalProperties: false,
|