@getpaseo/server 0.9.2 → 0.10.0-beta.1
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/dist/scripts/supervisor.js +51 -21
- package/dist/server/server/agent/agent-manager.js +22 -2
- package/dist/server/server/agent/agent-prompt.js +12 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts +3 -0
- package/dist/server/server/agent/mcp-shared.d.ts +5 -1
- package/dist/server/server/agent/mcp-shared.js +2 -1
- package/dist/server/server/agent/provider-registry.js +2 -2
- package/dist/server/server/agent/providers/acp-agent.js +20 -11
- package/dist/server/server/agent/providers/claude/agent.d.ts +1 -2
- package/dist/server/server/agent/providers/claude/agent.js +10 -9
- package/dist/server/server/agent/providers/claude/models.d.ts +1 -1
- package/dist/server/server/agent/providers/claude/models.js +1 -5
- package/dist/server/server/agent/providers/claude/project-dir.d.ts +1 -0
- package/dist/server/server/agent/providers/claude/project-dir.js +5 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +6 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +10 -1
- package/dist/server/server/agent/providers/generic-acp-agent.js +3 -1
- package/dist/server/server/agent/providers/omp/agent.js +23 -6
- package/dist/server/server/agent/providers/omp/rpc-types.d.ts +1 -0
- package/dist/server/server/agent/providers/omp/rpc-types.js +1 -0
- package/dist/server/server/agent/providers/opencode/bridge.d.ts +5 -1
- package/dist/server/server/agent/providers/opencode/bridge.js +56 -5
- package/dist/server/server/agent/providers/opencode/http-error.d.ts +8 -0
- package/dist/server/server/agent/providers/opencode/http-error.js +13 -0
- package/dist/server/server/agent/providers/opencode/runtime-client.d.ts +38 -0
- package/dist/server/server/agent/providers/opencode/runtime-client.js +131 -0
- package/dist/server/server/agent/providers/opencode/runtime-notice.d.ts +3 -0
- package/dist/server/server/agent/providers/opencode/runtime-notice.js +47 -0
- package/dist/server/server/agent/providers/opencode/server-manager.js +19 -8
- package/dist/server/server/agent/providers/opencode/v2/agent.d.ts +36 -0
- package/dist/server/server/agent/providers/opencode/v2/agent.js +208 -0
- package/dist/server/server/agent/providers/opencode/v2/api.d.ts +18 -0
- package/dist/server/server/agent/providers/opencode/v2/api.js +2 -0
- package/dist/server/server/agent/providers/opencode/v2/bridge-plugin.bundle.mjs +6693 -0
- package/dist/server/server/agent/providers/opencode/v2/capabilities.d.ts +3 -0
- package/dist/server/server/agent/providers/opencode/v2/capabilities.js +13 -0
- package/dist/server/server/agent/providers/opencode/v2/children.d.ts +20 -0
- package/dist/server/server/agent/providers/opencode/v2/children.js +75 -0
- package/dist/server/server/agent/providers/opencode/v2/commands.d.ts +4 -0
- package/dist/server/server/agent/providers/opencode/v2/commands.js +36 -0
- package/dist/server/server/agent/providers/opencode/v2/configuration.d.ts +8 -0
- package/dist/server/server/agent/providers/opencode/v2/configuration.js +64 -0
- package/dist/server/server/agent/providers/opencode/v2/history.d.ts +4 -0
- package/dist/server/server/agent/providers/opencode/v2/history.js +15 -0
- package/dist/server/server/agent/providers/opencode/v2/mapping.d.ts +7 -0
- package/dist/server/server/agent/providers/opencode/v2/mapping.js +42 -0
- package/dist/server/server/agent/providers/opencode/v2/permissions.d.ts +18 -0
- package/dist/server/server/agent/providers/opencode/v2/permissions.js +157 -0
- package/dist/server/server/agent/providers/opencode/v2/readiness.d.ts +18 -0
- package/dist/server/server/agent/providers/opencode/v2/readiness.js +51 -0
- package/dist/server/server/agent/providers/opencode/v2/runtime.d.ts +36 -0
- package/dist/server/server/agent/providers/opencode/v2/runtime.js +230 -0
- package/dist/server/server/agent/providers/opencode/v2/session.d.ts +84 -0
- package/dist/server/server/agent/providers/opencode/v2/session.js +465 -0
- package/dist/server/server/agent/providers/opencode/v2/structured-output.d.ts +8 -0
- package/dist/server/server/agent/providers/opencode/v2/structured-output.js +32 -0
- package/dist/server/server/agent/providers/opencode/v2/timeline.d.ts +28 -0
- package/dist/server/server/agent/providers/opencode/v2/timeline.js +154 -0
- package/dist/server/server/agent/providers/opencode/v2/turns.d.ts +40 -0
- package/dist/server/server/agent/providers/opencode/v2/turns.js +226 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts +26 -7
- package/dist/server/server/agent/providers/opencode-agent.js +100 -40
- package/dist/server/server/agent/providers/pi/agent.d.ts +6 -6
- package/dist/server/server/agent/providers/pi/agent.js +114 -165
- package/dist/server/server/agent/providers/pi/extensions/child-session.d.ts +4 -0
- package/dist/server/server/agent/providers/pi/extensions/child-session.js +59 -0
- package/dist/server/server/agent/providers/pi/extensions/contract.d.ts +68 -0
- package/dist/server/server/agent/providers/pi/extensions/contract.js +2 -0
- package/dist/server/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.js +159 -0
- package/dist/server/server/agent/providers/pi/extensions/host.d.ts +28 -0
- package/dist/server/server/agent/providers/pi/extensions/host.js +94 -0
- package/dist/server/server/agent/providers/pi/extensions/index.d.ts +7 -0
- package/dist/server/server/agent/providers/pi/extensions/index.js +6 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-ask-user/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-ask-user/index.js +138 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-example-todo/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-example-todo/index.js +38 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-goal-x/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-goal-x/index.js +63 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-mcp-adapter/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-mcp-adapter/index.js +48 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-subagents/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/pi-subagents/index.js +131 -0
- package/dist/server/server/agent/providers/pi/extensions/registry.d.ts +2 -0
- package/dist/server/server/agent/providers/pi/extensions/registry.js +21 -0
- package/dist/server/server/agent/providers/pi/extensions/rpiv-ask-user-question/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/rpiv-ask-user-question/index.js +180 -0
- package/dist/server/server/agent/providers/pi/extensions/rpiv-todo/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/rpiv-todo/index.js +51 -0
- package/dist/server/server/agent/providers/pi/extensions/subagent-fixture-test.d.ts +22 -0
- package/dist/server/server/agent/providers/pi/extensions/subagent-fixture-test.js +66 -0
- package/dist/server/server/agent/providers/pi/extensions/task-fixture-test.d.ts +4 -0
- package/dist/server/server/agent/providers/pi/extensions/task-fixture-test.js +79 -0
- package/dist/server/server/agent/providers/pi/extensions/tintinweb-pi-subagents/index.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/extensions/tintinweb-pi-subagents/index.js +149 -0
- package/dist/server/server/agent/providers/pi/history-mapper.d.ts +7 -2
- package/dist/server/server/agent/providers/pi/history-mapper.js +78 -35
- package/dist/server/server/agent/providers/pi/rpc-types.d.ts +3 -0
- package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +170 -0
- package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +355 -0
- package/dist/server/server/agent/providers/pi/tool-call-mapper.d.ts +1 -6
- package/dist/server/server/agent/providers/pi/tool-call-mapper.js +1 -97
- package/dist/server/server/agent/tools/paseo-tools.js +22 -15
- package/dist/server/server/auth.d.ts +1 -2
- package/dist/server/server/auth.js +8 -14
- package/dist/server/server/bootstrap.js +10 -2
- package/dist/server/server/daemon-instance.d.ts +1 -0
- package/dist/server/server/daemon-instance.js +1 -0
- package/dist/server/server/exports.d.ts +1 -1
- package/dist/server/server/exports.js +1 -1
- package/dist/server/server/local-credential.d.ts +6 -0
- package/dist/server/server/local-credential.js +82 -0
- package/dist/server/server/session-admission-auth.d.ts +16 -0
- package/dist/server/server/session-admission-auth.js +26 -0
- package/dist/server/server/websocket-server.d.ts +6 -1
- package/dist/server/server/websocket-server.js +132 -55
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-bridge-f41bce08c3c4ac2de0652d20234d19e9.js → desktop-attachment-bridge-9da07adb97f5e0eb726f7762afee43f5.js} +1 -1
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-bridge-f41bce08c3c4ac2de0652d20234d19e9.js.br → desktop-attachment-bridge-9da07adb97f5e0eb726f7762afee43f5.js.br} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-9da07adb97f5e0eb726f7762afee43f5.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-store-9e271e161480b0d1dd35e58616a37341.js → desktop-attachment-store-d5d3402a752965d3037191484127123b.js} +1 -1
- package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-store-9e271e161480b0d1dd35e58616a37341.js.br → desktop-attachment-store-d5d3402a752965d3037191484127123b.js.br} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-d5d3402a752965d3037191484127123b.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-8868523fc6d311c5da524c067d1cd426.js → index-69ce3c243032c3bedfc6f2ad379a4cdd.js} +1579 -1575
- package/dist/server/web-ui/_expo/static/js/web/index-69ce3c243032c3bedfc6f2ad379a4cdd.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-69ce3c243032c3bedfc6f2ad379a4cdd.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{indexeddb-attachment-store-369cdb67f5856fd2c72d640df0b7b79e.js → indexeddb-attachment-store-3afc19e43c8152bd3c6d71b3ea8776dc.js} +1 -1
- package/dist/server/web-ui/_expo/static/js/web/{indexeddb-attachment-store-369cdb67f5856fd2c72d640df0b7b79e.js.br → indexeddb-attachment-store-3afc19e43c8152bd3c6d71b3ea8776dc.js.br} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-3afc19e43c8152bd3c6d71b3ea8776dc.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{native-file-attachment-store-bcd89f6ce714279a8f4fa0e024f3a610.js → native-file-attachment-store-187a9d5c32188f357dbbf037a0f89073.js} +3 -3
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-187a9d5c32188f357dbbf037a0f89073.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-187a9d5c32188f357dbbf037a0f89073.js.gz +0 -0
- package/dist/server/web-ui/index.html +1 -1
- package/dist/server/web-ui/index.html.br +0 -0
- package/dist/server/web-ui/index.html.gz +0 -0
- package/dist/src/server/daemon-instance.js +1 -0
- package/dist/src/server/local-credential.js +82 -0
- package/package.json +8 -7
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-f41bce08c3c4ac2de0652d20234d19e9.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-9e271e161480b0d1dd35e58616a37341.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-8868523fc6d311c5da524c067d1cd426.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-8868523fc6d311c5da524c067d1cd426.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-369cdb67f5856fd2c72d640df0b7b79e.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-bcd89f6ce714279a8f4fa0e024f3a610.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-bcd89f6ce714279a8f4fa0e024f3a610.js.gz +0 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { extractTextFromToolResult } from "../../tool-call-mapper.js";
|
|
3
|
+
const Args = z
|
|
4
|
+
.object({
|
|
5
|
+
agent: z.string().trim().min(1).optional(),
|
|
6
|
+
task: z.string().trim().min(1).optional(),
|
|
7
|
+
async: z.boolean().optional(),
|
|
8
|
+
action: z.unknown().optional(),
|
|
9
|
+
})
|
|
10
|
+
.passthrough();
|
|
11
|
+
const Row = z
|
|
12
|
+
.object({
|
|
13
|
+
index: z.number().int().nonnegative().optional(),
|
|
14
|
+
agent: z.string().trim().min(1),
|
|
15
|
+
exitCode: z.number().optional(),
|
|
16
|
+
success: z.boolean().optional(),
|
|
17
|
+
sessionFile: z.string().trim().min(1).optional(),
|
|
18
|
+
})
|
|
19
|
+
.passthrough();
|
|
20
|
+
const Completion = z
|
|
21
|
+
.object({ runId: z.string().trim().min(1), results: z.array(Row) })
|
|
22
|
+
.passthrough();
|
|
23
|
+
const Details = z
|
|
24
|
+
.object({
|
|
25
|
+
mode: z.string(),
|
|
26
|
+
runId: z.string().optional(),
|
|
27
|
+
asyncId: z.string().optional(),
|
|
28
|
+
results: z.array(Row),
|
|
29
|
+
completions: z.array(Completion).optional(),
|
|
30
|
+
})
|
|
31
|
+
.passthrough();
|
|
32
|
+
export const piSubagents = {
|
|
33
|
+
id: "pi-subagents",
|
|
34
|
+
createSession: () => {
|
|
35
|
+
const callsByRun = new Map();
|
|
36
|
+
const readSessions = new Set();
|
|
37
|
+
const collectRows = (owner, rows, description, failed = false) => {
|
|
38
|
+
const subagents = [];
|
|
39
|
+
const childSessions = [];
|
|
40
|
+
for (const [index, row] of rows.entries()) {
|
|
41
|
+
const id = rows.length === 1 ? owner : `${owner}:${row.index ?? index}`;
|
|
42
|
+
subagents.push({
|
|
43
|
+
type: "upsert",
|
|
44
|
+
id,
|
|
45
|
+
title: row.agent,
|
|
46
|
+
description,
|
|
47
|
+
toolCallId: owner,
|
|
48
|
+
status: failed || row.success === false || (row.success !== true && row.exitCode !== 0)
|
|
49
|
+
? "failed"
|
|
50
|
+
: "completed",
|
|
51
|
+
});
|
|
52
|
+
if (row.sessionFile && !readSessions.has(row.sessionFile)) {
|
|
53
|
+
readSessions.add(row.sessionFile);
|
|
54
|
+
childSessions.push({ id, file: row.sessionFile });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { subagents, childSessions };
|
|
58
|
+
};
|
|
59
|
+
const mapWait = (call) => {
|
|
60
|
+
if (call.status === "running")
|
|
61
|
+
return undefined;
|
|
62
|
+
const details = Details.safeParse(typeof call.result === "object" ? call.result?.details : null);
|
|
63
|
+
if (!details.success || !details.data.completions)
|
|
64
|
+
return undefined;
|
|
65
|
+
const subagents = [];
|
|
66
|
+
const childSessions = [];
|
|
67
|
+
for (const completion of details.data.completions) {
|
|
68
|
+
const owner = callsByRun.get(completion.runId);
|
|
69
|
+
if (!owner)
|
|
70
|
+
continue;
|
|
71
|
+
const mapped = collectRows(owner, completion.results);
|
|
72
|
+
subagents.push(...mapped.subagents);
|
|
73
|
+
childSessions.push(...mapped.childSessions);
|
|
74
|
+
}
|
|
75
|
+
return subagents.length ? { subagents, childSessions } : undefined;
|
|
76
|
+
};
|
|
77
|
+
const mapSpawn = (call) => {
|
|
78
|
+
const args = Args.safeParse(call.args);
|
|
79
|
+
if (!args.success || args.data.action !== undefined || !args.data.agent || !args.data.task)
|
|
80
|
+
return undefined;
|
|
81
|
+
const detail = {
|
|
82
|
+
type: "sub_agent",
|
|
83
|
+
subAgentType: args.data.agent,
|
|
84
|
+
description: args.data.task,
|
|
85
|
+
log: extractTextFromToolResult(call.result)?.trim() ?? "",
|
|
86
|
+
};
|
|
87
|
+
const base = {
|
|
88
|
+
type: "upsert",
|
|
89
|
+
id: call.callId,
|
|
90
|
+
title: args.data.agent,
|
|
91
|
+
description: args.data.task,
|
|
92
|
+
toolCallId: call.callId,
|
|
93
|
+
};
|
|
94
|
+
if (call.status === "running")
|
|
95
|
+
return { detail, subagents: [{ ...base, status: "running" }] };
|
|
96
|
+
const details = Details.safeParse(typeof call.result === "object" ? call.result?.details : null);
|
|
97
|
+
if (!details.success || details.data.mode === "management")
|
|
98
|
+
return call.status === "failed"
|
|
99
|
+
? { detail, subagents: [{ ...base, status: "failed" }] }
|
|
100
|
+
: { detail };
|
|
101
|
+
if (details.data.runId)
|
|
102
|
+
callsByRun.set(details.data.runId, call.callId);
|
|
103
|
+
if (details.data.results.length === 0 && (details.data.asyncId || args.data.async))
|
|
104
|
+
return {
|
|
105
|
+
detail,
|
|
106
|
+
subagents: [
|
|
107
|
+
{
|
|
108
|
+
...base,
|
|
109
|
+
status: call.status === "failed" ? "failed" : "running",
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
if (details.data.results.length === 0 && call.status === "failed")
|
|
114
|
+
return { detail, subagents: [{ ...base, status: "failed" }] };
|
|
115
|
+
return {
|
|
116
|
+
detail,
|
|
117
|
+
...collectRows(call.callId, details.data.results, args.data.task, call.status === "failed"),
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
mapToolCall(call) {
|
|
122
|
+
if (call.toolName === "bg_wait")
|
|
123
|
+
return mapWait(call);
|
|
124
|
+
if (call.toolName === "subagent")
|
|
125
|
+
return mapSpawn(call);
|
|
126
|
+
return undefined;
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { piAskUser } from "./pi-ask-user/index.js";
|
|
2
|
+
import { piSubagents } from "./pi-subagents/index.js";
|
|
3
|
+
import { piMcpAdapter } from "./pi-mcp-adapter/index.js";
|
|
4
|
+
import { rpivTodo } from "./rpiv-todo/index.js";
|
|
5
|
+
import { piExampleTodo } from "./pi-example-todo/index.js";
|
|
6
|
+
import { piGoalX } from "./pi-goal-x/index.js";
|
|
7
|
+
import { tintinwebPiSubagents } from "./tintinweb-pi-subagents/index.js";
|
|
8
|
+
import { gotgenesPiSubagents } from "./gotgenes-pi-subagents/index.js";
|
|
9
|
+
import { rpivAskUserQuestion } from "./rpiv-ask-user-question/index.js";
|
|
10
|
+
export const piExtensions = [
|
|
11
|
+
piAskUser, // Dialog correlation
|
|
12
|
+
piSubagents, // Delegation calls
|
|
13
|
+
tintinwebPiSubagents,
|
|
14
|
+
gotgenesPiSubagents,
|
|
15
|
+
piMcpAdapter, // MCP proxy names
|
|
16
|
+
rpivTodo,
|
|
17
|
+
piExampleTodo,
|
|
18
|
+
piGoalX,
|
|
19
|
+
rpivAskUserQuestion,
|
|
20
|
+
];
|
|
21
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const Params = z
|
|
3
|
+
.object({
|
|
4
|
+
questions: z
|
|
5
|
+
.array(z.object({
|
|
6
|
+
question: z.string(),
|
|
7
|
+
header: z.string(),
|
|
8
|
+
options: z
|
|
9
|
+
.array(z.object({
|
|
10
|
+
label: z.string(),
|
|
11
|
+
description: z.string(),
|
|
12
|
+
preview: z.string().optional(),
|
|
13
|
+
}))
|
|
14
|
+
.min(2)
|
|
15
|
+
.max(4),
|
|
16
|
+
multiSelect: z.boolean().optional(),
|
|
17
|
+
}))
|
|
18
|
+
.min(1)
|
|
19
|
+
.max(4),
|
|
20
|
+
})
|
|
21
|
+
.refine(({ questions }) => {
|
|
22
|
+
const texts = questions.map((question) => question.question);
|
|
23
|
+
return (new Set(texts).size === texts.length &&
|
|
24
|
+
questions.every((question) => {
|
|
25
|
+
const labels = question.options.map((option) => option.label);
|
|
26
|
+
return (new Set(labels).size === labels.length &&
|
|
27
|
+
labels.every((label) => label !== "Other" && label !== "Type something." && label !== "Next"));
|
|
28
|
+
}));
|
|
29
|
+
});
|
|
30
|
+
const Result = z.object({
|
|
31
|
+
answers: z.array(z.object({
|
|
32
|
+
question: z.string(),
|
|
33
|
+
kind: z.enum(["option", "custom", "multi"]),
|
|
34
|
+
answer: z.string().nullable(),
|
|
35
|
+
selected: z.array(z.string()).optional(),
|
|
36
|
+
})),
|
|
37
|
+
cancelled: z.boolean(),
|
|
38
|
+
error: z.string().optional(),
|
|
39
|
+
});
|
|
40
|
+
const PERMISSION_PREFIX = "rpiv-question:";
|
|
41
|
+
function answerFor(answers, question) {
|
|
42
|
+
const answer = answers[question.header];
|
|
43
|
+
return typeof answer === "string" ? answer : null;
|
|
44
|
+
}
|
|
45
|
+
function replyToDialog(dialog, question, answer, customInput) {
|
|
46
|
+
const options = z.array(z.string()).safeParse(dialog.options).data;
|
|
47
|
+
if (customInput)
|
|
48
|
+
return { response: { value: answer }, customInput: false };
|
|
49
|
+
if (question.multiSelect) {
|
|
50
|
+
const labels = answer.split(", ");
|
|
51
|
+
const indices = labels.map((label) => question.options.findIndex((option) => option.label === label));
|
|
52
|
+
if (indices.every((index) => index >= 0)) {
|
|
53
|
+
return {
|
|
54
|
+
response: { value: indices.map((index) => index + 1).join(",") },
|
|
55
|
+
customInput: false,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return { response: { value: answer }, customInput: false };
|
|
59
|
+
}
|
|
60
|
+
const index = question.options.findIndex((option) => option.label === answer);
|
|
61
|
+
if (index >= 0) {
|
|
62
|
+
return {
|
|
63
|
+
response: {
|
|
64
|
+
value: options?.[index] ?? `${index + 1}. ${answer} — ${question.options[index].description}`,
|
|
65
|
+
},
|
|
66
|
+
customInput: false,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
response: {
|
|
71
|
+
value: options?.[question.options.length] ?? `${question.options.length + 1}. Type something.`,
|
|
72
|
+
},
|
|
73
|
+
customInput: true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export const rpivAskUserQuestion = {
|
|
77
|
+
id: "rpiv-ask-user-question",
|
|
78
|
+
createSession() {
|
|
79
|
+
let active = null;
|
|
80
|
+
function answerDialog(dialog) {
|
|
81
|
+
if (!active || active.cancelled)
|
|
82
|
+
return { cancelled: true };
|
|
83
|
+
const question = active.questions[active.index];
|
|
84
|
+
if (!question || !active.answers)
|
|
85
|
+
return { cancelled: true };
|
|
86
|
+
const answer = answerFor(active.answers, question);
|
|
87
|
+
if (answer === null)
|
|
88
|
+
return { cancelled: true };
|
|
89
|
+
const mapped = replyToDialog(dialog, question, answer, active.customInput);
|
|
90
|
+
active.customInput = mapped.customInput;
|
|
91
|
+
if (!active.customInput)
|
|
92
|
+
active.index++;
|
|
93
|
+
return mapped.response;
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
onToolStart(call, provider) {
|
|
97
|
+
if (call.toolName !== "ask_user_question")
|
|
98
|
+
return undefined;
|
|
99
|
+
const parsed = Params.safeParse(call.args);
|
|
100
|
+
if (!parsed.success)
|
|
101
|
+
return undefined;
|
|
102
|
+
active = {
|
|
103
|
+
callId: call.callId,
|
|
104
|
+
questions: parsed.data.questions,
|
|
105
|
+
answers: null,
|
|
106
|
+
cancelled: false,
|
|
107
|
+
index: 0,
|
|
108
|
+
customInput: false,
|
|
109
|
+
deferred: null,
|
|
110
|
+
};
|
|
111
|
+
return {
|
|
112
|
+
id: `${PERMISSION_PREFIX}${call.callId}`,
|
|
113
|
+
provider,
|
|
114
|
+
name: "Pi ask_user_question",
|
|
115
|
+
kind: "question",
|
|
116
|
+
title: parsed.data.questions[0].question,
|
|
117
|
+
input: {
|
|
118
|
+
questions: parsed.data.questions.map((question) => ({
|
|
119
|
+
question: question.question,
|
|
120
|
+
header: question.header,
|
|
121
|
+
options: question.options,
|
|
122
|
+
multiSelect: question.multiSelect ?? false,
|
|
123
|
+
allowOther: true,
|
|
124
|
+
})),
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
onToolEnd(call) {
|
|
129
|
+
if (active?.callId === call.callId)
|
|
130
|
+
active = null;
|
|
131
|
+
},
|
|
132
|
+
mapDialog(dialog) {
|
|
133
|
+
if (!active || (dialog.method !== "select" && dialog.method !== "input"))
|
|
134
|
+
return undefined;
|
|
135
|
+
if (!active.answers && !active.cancelled) {
|
|
136
|
+
active.deferred = dialog;
|
|
137
|
+
return { type: "deferred" };
|
|
138
|
+
}
|
|
139
|
+
return { type: "response", response: answerDialog(dialog) };
|
|
140
|
+
},
|
|
141
|
+
respondToPermission(request, response) {
|
|
142
|
+
if (!active || request.id !== `${PERMISSION_PREFIX}${active.callId}`)
|
|
143
|
+
return undefined;
|
|
144
|
+
const answers = response.behavior === "allow" ? response.updatedInput?.answers : undefined;
|
|
145
|
+
active.answers =
|
|
146
|
+
answers && typeof answers === "object" && !Array.isArray(answers)
|
|
147
|
+
? answers
|
|
148
|
+
: null;
|
|
149
|
+
active.cancelled = response.behavior === "deny" || !active.answers;
|
|
150
|
+
const deferred = active.deferred;
|
|
151
|
+
active.deferred = null;
|
|
152
|
+
return {
|
|
153
|
+
responses: deferred ? [{ id: deferred.id, response: answerDialog(deferred) }] : [],
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
mapToolCall(call) {
|
|
157
|
+
if (call.toolName !== "ask_user_question" ||
|
|
158
|
+
!call.result ||
|
|
159
|
+
typeof call.result === "string")
|
|
160
|
+
return undefined;
|
|
161
|
+
const result = Result.safeParse(call.result.details);
|
|
162
|
+
if (!result.success)
|
|
163
|
+
return undefined;
|
|
164
|
+
return {
|
|
165
|
+
detail: {
|
|
166
|
+
type: "plain_text",
|
|
167
|
+
label: "Questions",
|
|
168
|
+
text: result.data.error ??
|
|
169
|
+
(result.data.cancelled
|
|
170
|
+
? "Cancelled"
|
|
171
|
+
: result.data.answers
|
|
172
|
+
.map((answer) => `${answer.question}: ${answer.kind === "multi" ? (answer.selected ?? []).join(", ") : (answer.answer ?? "")}`)
|
|
173
|
+
.join("\n")),
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const Details = z.object({
|
|
3
|
+
action: z.enum(["create", "update", "list", "get", "delete", "clear"]),
|
|
4
|
+
params: z.record(z.string(), z.unknown()),
|
|
5
|
+
nextId: z.number().int(),
|
|
6
|
+
tasks: z.array(z.object({
|
|
7
|
+
id: z.number().int(),
|
|
8
|
+
subject: z.string(),
|
|
9
|
+
status: z.enum(["pending", "in_progress", "completed", "deleted"]),
|
|
10
|
+
activeForm: z.string().optional(),
|
|
11
|
+
})),
|
|
12
|
+
error: z.string().optional(),
|
|
13
|
+
});
|
|
14
|
+
export const rpivTodo = {
|
|
15
|
+
id: "rpiv-todo",
|
|
16
|
+
createSession: () => ({
|
|
17
|
+
mapToolCall(call) {
|
|
18
|
+
if (call.toolName !== "todo" ||
|
|
19
|
+
call.status !== "completed" ||
|
|
20
|
+
!call.result ||
|
|
21
|
+
typeof call.result === "string")
|
|
22
|
+
return undefined;
|
|
23
|
+
const parsed = Details.safeParse(call.result.details);
|
|
24
|
+
if (!parsed.success)
|
|
25
|
+
return undefined;
|
|
26
|
+
return {
|
|
27
|
+
timeline: parsed.data.error
|
|
28
|
+
? []
|
|
29
|
+
: [
|
|
30
|
+
{
|
|
31
|
+
type: "todo",
|
|
32
|
+
items: parsed.data.tasks.flatMap((task) => {
|
|
33
|
+
if (task.status === "deleted")
|
|
34
|
+
return [];
|
|
35
|
+
const item = {
|
|
36
|
+
id: String(task.id),
|
|
37
|
+
text: task.subject,
|
|
38
|
+
status: task.status,
|
|
39
|
+
completed: task.status === "completed",
|
|
40
|
+
};
|
|
41
|
+
if (task.activeForm)
|
|
42
|
+
item.activeForm = task.activeForm;
|
|
43
|
+
return [item];
|
|
44
|
+
}),
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AgentStreamEvent } from "../../../agent-sdk-types.js";
|
|
2
|
+
import type { PiAgentMessage, PiAgentSessionEvent } from "../rpc-types.js";
|
|
3
|
+
interface Fixture {
|
|
4
|
+
provenance: {
|
|
5
|
+
package: string;
|
|
6
|
+
version: string;
|
|
7
|
+
sourceCommit: string;
|
|
8
|
+
piVersion: string;
|
|
9
|
+
captureDate: string;
|
|
10
|
+
};
|
|
11
|
+
events: PiAgentSessionEvent[];
|
|
12
|
+
messages: PiAgentMessage[];
|
|
13
|
+
}
|
|
14
|
+
export declare function readSubagentFixture(path: URL, replaceSessionFile?: {
|
|
15
|
+
from: string;
|
|
16
|
+
to: string;
|
|
17
|
+
}): Fixture;
|
|
18
|
+
export declare function verifySubagentFixture(fixture: Fixture): Promise<Extract<AgentStreamEvent, {
|
|
19
|
+
type: "provider_subagent";
|
|
20
|
+
}>[]>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=subagent-fixture-test.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import pino from "pino";
|
|
3
|
+
import { expect, vi } from "vitest";
|
|
4
|
+
import { PiRpcAgentClient } from "../agent.js";
|
|
5
|
+
import { streamPiHistory } from "../history-mapper.js";
|
|
6
|
+
import { FakePi } from "../test-utils/fake-pi.js";
|
|
7
|
+
export function readSubagentFixture(path, replaceSessionFile) {
|
|
8
|
+
let source = readFileSync(path, "utf8");
|
|
9
|
+
if (replaceSessionFile)
|
|
10
|
+
source = source.replaceAll(replaceSessionFile.from, JSON.stringify(replaceSessionFile.to).slice(1, -1));
|
|
11
|
+
const fixture = JSON.parse(source);
|
|
12
|
+
expect(fixture.provenance).toEqual({
|
|
13
|
+
package: expect.any(String),
|
|
14
|
+
version: expect.any(String),
|
|
15
|
+
sourceCommit: expect.any(String),
|
|
16
|
+
piVersion: expect.any(String),
|
|
17
|
+
captureDate: expect.any(String),
|
|
18
|
+
});
|
|
19
|
+
return fixture;
|
|
20
|
+
}
|
|
21
|
+
export async function verifySubagentFixture(fixture) {
|
|
22
|
+
const pi = new FakePi();
|
|
23
|
+
const client = new PiRpcAgentClient({ logger: pino({ level: "silent" }), runtime: pi });
|
|
24
|
+
const session = await client.createSession({
|
|
25
|
+
provider: "pi",
|
|
26
|
+
cwd: "/tmp/paseo-pi-subagent-fixture",
|
|
27
|
+
});
|
|
28
|
+
const live = [];
|
|
29
|
+
session.subscribe((event) => live.push(event));
|
|
30
|
+
try {
|
|
31
|
+
await session.startTurn("Delegate work");
|
|
32
|
+
for (const event of fixture.events)
|
|
33
|
+
pi.latestSession().emit(event);
|
|
34
|
+
const immediate = live.filter((event) => event.type === "provider_subagent");
|
|
35
|
+
expect(immediate.some((event) => event.event.type === "timeline")).toBe(false);
|
|
36
|
+
pi.latestSession().finishTurn();
|
|
37
|
+
const replay = [];
|
|
38
|
+
for await (const event of streamPiHistory("pi", fixture.messages))
|
|
39
|
+
replay.push(event);
|
|
40
|
+
const replaySubagents = replay.filter((event) => event.type === "provider_subagent");
|
|
41
|
+
await vi.waitFor(() => {
|
|
42
|
+
expect(live.filter((event) => event.type === "provider_subagent")).toEqual(replaySubagents);
|
|
43
|
+
});
|
|
44
|
+
for (const message of fixture.messages) {
|
|
45
|
+
if (message.role !== "custom" || typeof message.content !== "string")
|
|
46
|
+
continue;
|
|
47
|
+
const assistantText = {
|
|
48
|
+
type: "assistant_message",
|
|
49
|
+
text: message.content,
|
|
50
|
+
};
|
|
51
|
+
expect(live).toContainEqual(expect.objectContaining({ type: "timeline", item: assistantText }));
|
|
52
|
+
expect(replay).toContainEqual(expect.objectContaining({ type: "timeline", item: assistantText }));
|
|
53
|
+
}
|
|
54
|
+
const liveSubagents = live.filter((event) => event.type === "provider_subagent");
|
|
55
|
+
const firstTimeline = liveSubagents.findIndex((event) => event.event.type === "timeline");
|
|
56
|
+
if (firstTimeline !== -1) {
|
|
57
|
+
expect(liveSubagents.slice(0, firstTimeline).some((event) => event.event.type === "upsert")).toBe(true);
|
|
58
|
+
}
|
|
59
|
+
expect(liveSubagents).toEqual(replaySubagents);
|
|
60
|
+
return liveSubagents;
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
await session.close();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=subagent-fixture-test.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PiExtensionToolCall } from "./contract.js";
|
|
2
|
+
export declare function capturedToolCall(path: URL, index?: number): PiExtensionToolCall;
|
|
3
|
+
export declare function verifyTaskFixture(path: URL, expectedStatuses: string[]): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=task-fixture-test.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import pino from "pino";
|
|
3
|
+
import { expect } from "vitest";
|
|
4
|
+
import { PiRpcAgentClient } from "../agent.js";
|
|
5
|
+
import { PiHistoryMapper } from "../history-mapper.js";
|
|
6
|
+
import { FakePi } from "../test-utils/fake-pi.js";
|
|
7
|
+
import { parseToolResult } from "../tool-call-mapper.js";
|
|
8
|
+
import { createPiExtensionHost } from "./index.js";
|
|
9
|
+
function todos(events) {
|
|
10
|
+
return events.flatMap((event) => event.type === "timeline" && event.item.type === "todo" ? [event.item] : []);
|
|
11
|
+
}
|
|
12
|
+
export function capturedToolCall(path, index = 0) {
|
|
13
|
+
const fixture = JSON.parse(readFileSync(path, "utf8"));
|
|
14
|
+
const ends = fixture.events.filter((event) => event.type === "tool_execution_end");
|
|
15
|
+
const end = ends[index];
|
|
16
|
+
if (!end)
|
|
17
|
+
throw new Error(`Missing captured tool result ${index}`);
|
|
18
|
+
const start = fixture.events.find((event) => event.type === "tool_execution_start" && event.toolCallId === end.toolCallId);
|
|
19
|
+
if (!start || start.type !== "tool_execution_start")
|
|
20
|
+
throw new Error("Missing captured tool start");
|
|
21
|
+
return {
|
|
22
|
+
callId: end.toolCallId,
|
|
23
|
+
toolName: end.toolName,
|
|
24
|
+
args: start.args,
|
|
25
|
+
status: end.isError ? "failed" : "completed",
|
|
26
|
+
result: parseToolResult(end.result),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export async function verifyTaskFixture(path, expectedStatuses) {
|
|
30
|
+
const fixture = JSON.parse(readFileSync(path, "utf8"));
|
|
31
|
+
expect(fixture.provenance).toEqual(expect.objectContaining({
|
|
32
|
+
package: expect.any(String),
|
|
33
|
+
version: expect.any(String),
|
|
34
|
+
sourceCommit: expect.any(String),
|
|
35
|
+
piVersion: expect.any(String),
|
|
36
|
+
captureDate: expect.any(String),
|
|
37
|
+
}));
|
|
38
|
+
const host = createPiExtensionHost();
|
|
39
|
+
const starts = new Map();
|
|
40
|
+
const mapped = [];
|
|
41
|
+
for (const event of fixture.events) {
|
|
42
|
+
if (event.type === "tool_execution_start")
|
|
43
|
+
starts.set(event.toolCallId, event);
|
|
44
|
+
if (event.type !== "tool_execution_end")
|
|
45
|
+
continue;
|
|
46
|
+
const start = starts.get(event.toolCallId);
|
|
47
|
+
expect(start).toBeDefined();
|
|
48
|
+
const mapping = host.mapToolCall({
|
|
49
|
+
callId: event.toolCallId,
|
|
50
|
+
toolName: event.toolName,
|
|
51
|
+
args: start?.args,
|
|
52
|
+
status: event.isError ? "failed" : "completed",
|
|
53
|
+
result: parseToolResult(event.result),
|
|
54
|
+
});
|
|
55
|
+
mapped.push(...(mapping?.timeline ?? []).filter((item) => item.type === "todo"));
|
|
56
|
+
}
|
|
57
|
+
expect(mapped.map((item) => item.items.map((task) => task.status))).toEqual(expectedStatuses.map((status) => status.split(",")));
|
|
58
|
+
const pi = new FakePi();
|
|
59
|
+
const client = new PiRpcAgentClient({ logger: pino({ level: "silent" }), runtime: pi });
|
|
60
|
+
const session = await client.createSession({ provider: "pi", cwd: "/tmp/paseo-pi-task-fixture" });
|
|
61
|
+
const live = [];
|
|
62
|
+
session.subscribe((event) => live.push(event));
|
|
63
|
+
try {
|
|
64
|
+
await session.startTurn("Use tasks");
|
|
65
|
+
for (const event of fixture.events)
|
|
66
|
+
pi.latestSession().emit(event);
|
|
67
|
+
pi.latestSession().finishTurn();
|
|
68
|
+
expect(todos(live)).toEqual(mapped);
|
|
69
|
+
const replay = new PiHistoryMapper("pi").mapMessages(fixture.messages);
|
|
70
|
+
expect(todos(replay)).toEqual(mapped);
|
|
71
|
+
expect(replay.filter((event) => event.type === "timeline" &&
|
|
72
|
+
event.item.type === "tool_call" &&
|
|
73
|
+
event.item.status === "completed")).toHaveLength(fixture.events.filter((event) => event.type === "tool_execution_end").length);
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
await session.close();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=task-fixture-test.js.map
|