@getpaseo/server 0.9.2 → 0.10.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.
Files changed (148) hide show
  1. package/dist/scripts/supervisor.js +51 -21
  2. package/dist/server/server/agent/agent-manager.js +22 -2
  3. package/dist/server/server/agent/agent-prompt.js +12 -0
  4. package/dist/server/server/agent/agent-sdk-types.d.ts +3 -0
  5. package/dist/server/server/agent/mcp-shared.d.ts +5 -1
  6. package/dist/server/server/agent/mcp-shared.js +2 -1
  7. package/dist/server/server/agent/provider-launch-config.d.ts +2 -2
  8. package/dist/server/server/agent/provider-launch-config.js +1 -1
  9. package/dist/server/server/agent/provider-registry.js +2 -2
  10. package/dist/server/server/agent/providers/acp-agent.js +20 -11
  11. package/dist/server/server/agent/providers/claude/agent.d.ts +1 -2
  12. package/dist/server/server/agent/providers/claude/agent.js +10 -9
  13. package/dist/server/server/agent/providers/claude/models.d.ts +1 -1
  14. package/dist/server/server/agent/providers/claude/models.js +1 -5
  15. package/dist/server/server/agent/providers/claude/project-dir.d.ts +1 -0
  16. package/dist/server/server/agent/providers/claude/project-dir.js +5 -1
  17. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +6 -1
  18. package/dist/server/server/agent/providers/codex-app-server-agent.js +10 -1
  19. package/dist/server/server/agent/providers/generic-acp-agent.js +3 -1
  20. package/dist/server/server/agent/providers/omp/agent.js +23 -6
  21. package/dist/server/server/agent/providers/omp/rpc-types.d.ts +1 -0
  22. package/dist/server/server/agent/providers/omp/rpc-types.js +1 -0
  23. package/dist/server/server/agent/providers/opencode/bridge.d.ts +5 -1
  24. package/dist/server/server/agent/providers/opencode/bridge.js +56 -5
  25. package/dist/server/server/agent/providers/opencode/http-error.d.ts +8 -0
  26. package/dist/server/server/agent/providers/opencode/http-error.js +13 -0
  27. package/dist/server/server/agent/providers/opencode/runtime-client.d.ts +38 -0
  28. package/dist/server/server/agent/providers/opencode/runtime-client.js +131 -0
  29. package/dist/server/server/agent/providers/opencode/runtime-notice.d.ts +3 -0
  30. package/dist/server/server/agent/providers/opencode/runtime-notice.js +47 -0
  31. package/dist/server/server/agent/providers/opencode/server-manager.js +19 -8
  32. package/dist/server/server/agent/providers/opencode/v2/agent.d.ts +36 -0
  33. package/dist/server/server/agent/providers/opencode/v2/agent.js +211 -0
  34. package/dist/server/server/agent/providers/opencode/v2/api.d.ts +18 -0
  35. package/dist/server/server/agent/providers/opencode/v2/api.js +2 -0
  36. package/dist/server/server/agent/providers/opencode/v2/bridge-plugin.bundle.mjs +6693 -0
  37. package/dist/server/server/agent/providers/opencode/v2/capabilities.d.ts +3 -0
  38. package/dist/server/server/agent/providers/opencode/v2/capabilities.js +13 -0
  39. package/dist/server/server/agent/providers/opencode/v2/children.d.ts +20 -0
  40. package/dist/server/server/agent/providers/opencode/v2/children.js +75 -0
  41. package/dist/server/server/agent/providers/opencode/v2/commands.d.ts +4 -0
  42. package/dist/server/server/agent/providers/opencode/v2/commands.js +36 -0
  43. package/dist/server/server/agent/providers/opencode/v2/configuration.d.ts +8 -0
  44. package/dist/server/server/agent/providers/opencode/v2/configuration.js +64 -0
  45. package/dist/server/server/agent/providers/opencode/v2/history.d.ts +4 -0
  46. package/dist/server/server/agent/providers/opencode/v2/history.js +15 -0
  47. package/dist/server/server/agent/providers/opencode/v2/mapping.d.ts +7 -0
  48. package/dist/server/server/agent/providers/opencode/v2/mapping.js +42 -0
  49. package/dist/server/server/agent/providers/opencode/v2/permissions.d.ts +18 -0
  50. package/dist/server/server/agent/providers/opencode/v2/permissions.js +157 -0
  51. package/dist/server/server/agent/providers/opencode/v2/readiness.d.ts +18 -0
  52. package/dist/server/server/agent/providers/opencode/v2/readiness.js +51 -0
  53. package/dist/server/server/agent/providers/opencode/v2/runtime.d.ts +36 -0
  54. package/dist/server/server/agent/providers/opencode/v2/runtime.js +230 -0
  55. package/dist/server/server/agent/providers/opencode/v2/session.d.ts +84 -0
  56. package/dist/server/server/agent/providers/opencode/v2/session.js +468 -0
  57. package/dist/server/server/agent/providers/opencode/v2/structured-output.d.ts +8 -0
  58. package/dist/server/server/agent/providers/opencode/v2/structured-output.js +32 -0
  59. package/dist/server/server/agent/providers/opencode/v2/timeline.d.ts +28 -0
  60. package/dist/server/server/agent/providers/opencode/v2/timeline.js +154 -0
  61. package/dist/server/server/agent/providers/opencode/v2/turns.d.ts +40 -0
  62. package/dist/server/server/agent/providers/opencode/v2/turns.js +226 -0
  63. package/dist/server/server/agent/providers/opencode-agent.d.ts +26 -7
  64. package/dist/server/server/agent/providers/opencode-agent.js +100 -40
  65. package/dist/server/server/agent/providers/pi/agent.d.ts +6 -6
  66. package/dist/server/server/agent/providers/pi/agent.js +114 -165
  67. package/dist/server/server/agent/providers/pi/extensions/child-session.d.ts +4 -0
  68. package/dist/server/server/agent/providers/pi/extensions/child-session.js +59 -0
  69. package/dist/server/server/agent/providers/pi/extensions/contract.d.ts +68 -0
  70. package/dist/server/server/agent/providers/pi/extensions/contract.js +2 -0
  71. package/dist/server/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.d.ts +3 -0
  72. package/dist/server/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.js +159 -0
  73. package/dist/server/server/agent/providers/pi/extensions/host.d.ts +28 -0
  74. package/dist/server/server/agent/providers/pi/extensions/host.js +94 -0
  75. package/dist/server/server/agent/providers/pi/extensions/index.d.ts +7 -0
  76. package/dist/server/server/agent/providers/pi/extensions/index.js +6 -0
  77. package/dist/server/server/agent/providers/pi/extensions/pi-ask-user/index.d.ts +3 -0
  78. package/dist/server/server/agent/providers/pi/extensions/pi-ask-user/index.js +138 -0
  79. package/dist/server/server/agent/providers/pi/extensions/pi-example-todo/index.d.ts +3 -0
  80. package/dist/server/server/agent/providers/pi/extensions/pi-example-todo/index.js +38 -0
  81. package/dist/server/server/agent/providers/pi/extensions/pi-goal-x/index.d.ts +3 -0
  82. package/dist/server/server/agent/providers/pi/extensions/pi-goal-x/index.js +63 -0
  83. package/dist/server/server/agent/providers/pi/extensions/pi-mcp-adapter/index.d.ts +3 -0
  84. package/dist/server/server/agent/providers/pi/extensions/pi-mcp-adapter/index.js +48 -0
  85. package/dist/server/server/agent/providers/pi/extensions/pi-subagents/index.d.ts +3 -0
  86. package/dist/server/server/agent/providers/pi/extensions/pi-subagents/index.js +131 -0
  87. package/dist/server/server/agent/providers/pi/extensions/registry.d.ts +2 -0
  88. package/dist/server/server/agent/providers/pi/extensions/registry.js +21 -0
  89. package/dist/server/server/agent/providers/pi/extensions/rpiv-ask-user-question/index.d.ts +3 -0
  90. package/dist/server/server/agent/providers/pi/extensions/rpiv-ask-user-question/index.js +180 -0
  91. package/dist/server/server/agent/providers/pi/extensions/rpiv-todo/index.d.ts +3 -0
  92. package/dist/server/server/agent/providers/pi/extensions/rpiv-todo/index.js +51 -0
  93. package/dist/server/server/agent/providers/pi/extensions/subagent-fixture-test.d.ts +22 -0
  94. package/dist/server/server/agent/providers/pi/extensions/subagent-fixture-test.js +66 -0
  95. package/dist/server/server/agent/providers/pi/extensions/task-fixture-test.d.ts +4 -0
  96. package/dist/server/server/agent/providers/pi/extensions/task-fixture-test.js +79 -0
  97. package/dist/server/server/agent/providers/pi/extensions/tintinweb-pi-subagents/index.d.ts +3 -0
  98. package/dist/server/server/agent/providers/pi/extensions/tintinweb-pi-subagents/index.js +149 -0
  99. package/dist/server/server/agent/providers/pi/history-mapper.d.ts +7 -2
  100. package/dist/server/server/agent/providers/pi/history-mapper.js +78 -35
  101. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +3 -0
  102. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +170 -0
  103. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +355 -0
  104. package/dist/server/server/agent/providers/pi/tool-call-mapper.d.ts +1 -6
  105. package/dist/server/server/agent/providers/pi/tool-call-mapper.js +1 -97
  106. package/dist/server/server/agent/tools/paseo-tools.js +22 -15
  107. package/dist/server/server/auth.d.ts +1 -2
  108. package/dist/server/server/auth.js +8 -14
  109. package/dist/server/server/bootstrap.js +10 -2
  110. package/dist/server/server/daemon-instance.d.ts +1 -0
  111. package/dist/server/server/daemon-instance.js +1 -0
  112. package/dist/server/server/exports.d.ts +1 -1
  113. package/dist/server/server/exports.js +1 -1
  114. package/dist/server/server/local-credential.d.ts +6 -0
  115. package/dist/server/server/local-credential.js +82 -0
  116. package/dist/server/server/session-admission-auth.d.ts +16 -0
  117. package/dist/server/server/session-admission-auth.js +26 -0
  118. package/dist/server/server/websocket-server.d.ts +6 -1
  119. package/dist/server/server/websocket-server.js +132 -55
  120. package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-bridge-f41bce08c3c4ac2de0652d20234d19e9.js → desktop-attachment-bridge-9da07adb97f5e0eb726f7762afee43f5.js} +1 -1
  121. package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-bridge-f41bce08c3c4ac2de0652d20234d19e9.js.br → desktop-attachment-bridge-9da07adb97f5e0eb726f7762afee43f5.js.br} +0 -0
  122. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-9da07adb97f5e0eb726f7762afee43f5.js.gz +0 -0
  123. package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-store-9e271e161480b0d1dd35e58616a37341.js → desktop-attachment-store-d5d3402a752965d3037191484127123b.js} +1 -1
  124. package/dist/server/web-ui/_expo/static/js/web/{desktop-attachment-store-9e271e161480b0d1dd35e58616a37341.js.br → desktop-attachment-store-d5d3402a752965d3037191484127123b.js.br} +0 -0
  125. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-d5d3402a752965d3037191484127123b.js.gz +0 -0
  126. package/dist/server/web-ui/_expo/static/js/web/{index-8868523fc6d311c5da524c067d1cd426.js → index-136fa41d4adb7ec5c6392839cc354136.js} +1580 -1576
  127. package/dist/server/web-ui/_expo/static/js/web/index-136fa41d4adb7ec5c6392839cc354136.js.br +0 -0
  128. package/dist/server/web-ui/_expo/static/js/web/index-136fa41d4adb7ec5c6392839cc354136.js.gz +0 -0
  129. package/dist/server/web-ui/_expo/static/js/web/{indexeddb-attachment-store-369cdb67f5856fd2c72d640df0b7b79e.js → indexeddb-attachment-store-3afc19e43c8152bd3c6d71b3ea8776dc.js} +1 -1
  130. package/dist/server/web-ui/_expo/static/js/web/{indexeddb-attachment-store-369cdb67f5856fd2c72d640df0b7b79e.js.br → indexeddb-attachment-store-3afc19e43c8152bd3c6d71b3ea8776dc.js.br} +0 -0
  131. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-3afc19e43c8152bd3c6d71b3ea8776dc.js.gz +0 -0
  132. package/dist/server/web-ui/_expo/static/js/web/{native-file-attachment-store-bcd89f6ce714279a8f4fa0e024f3a610.js → native-file-attachment-store-187a9d5c32188f357dbbf037a0f89073.js} +3 -3
  133. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-187a9d5c32188f357dbbf037a0f89073.js.br +0 -0
  134. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-187a9d5c32188f357dbbf037a0f89073.js.gz +0 -0
  135. package/dist/server/web-ui/index.html +1 -1
  136. package/dist/server/web-ui/index.html.br +0 -0
  137. package/dist/server/web-ui/index.html.gz +0 -0
  138. package/dist/src/server/agent/provider-launch-config.js +1 -1
  139. package/dist/src/server/daemon-instance.js +1 -0
  140. package/dist/src/server/local-credential.js +82 -0
  141. package/package.json +8 -7
  142. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-bridge-f41bce08c3c4ac2de0652d20234d19e9.js.gz +0 -0
  143. package/dist/server/web-ui/_expo/static/js/web/desktop-attachment-store-9e271e161480b0d1dd35e58616a37341.js.gz +0 -0
  144. package/dist/server/web-ui/_expo/static/js/web/index-8868523fc6d311c5da524c067d1cd426.js.br +0 -0
  145. package/dist/server/web-ui/_expo/static/js/web/index-8868523fc6d311c5da524c067d1cd426.js.gz +0 -0
  146. package/dist/server/web-ui/_expo/static/js/web/indexeddb-attachment-store-369cdb67f5856fd2c72d640df0b7b79e.js.gz +0 -0
  147. package/dist/server/web-ui/_expo/static/js/web/native-file-attachment-store-bcd89f6ce714279a8f4fa0e024f3a610.js.br +0 -0
  148. 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,2 @@
1
+ export declare const piExtensions: import("./contract.js").PiExtension[];
2
+ //# sourceMappingURL=registry.d.ts.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,3 @@
1
+ import type { PiExtension } from "../contract.js";
2
+ export declare const rpivAskUserQuestion: PiExtension;
3
+ //# sourceMappingURL=index.d.ts.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,3 @@
1
+ import type { PiExtension } from "../contract.js";
2
+ export declare const rpivTodo: PiExtension;
3
+ //# sourceMappingURL=index.d.ts.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
@@ -0,0 +1,3 @@
1
+ import type { PiExtension } from "../contract.js";
2
+ export declare const tintinwebPiSubagents: PiExtension;
3
+ //# sourceMappingURL=index.d.ts.map