pi-roundtable-coding 0.8.0 → 0.9.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/CHANGELOG.md +9 -0
- package/README.md +15 -2
- package/package.json +3 -3
- package/src/coding-plugin.ts +63 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.1] - 2026-10-10
|
|
6
|
+
|
|
7
|
+
- Release in lockstep with pi-roundtable 0.9.1; no package-specific behavior changes.
|
|
8
|
+
|
|
9
|
+
## [0.9.0] - 2026-10-09
|
|
10
|
+
|
|
11
|
+
- The extra `skill_list` is offered by whom a conversation serves, not by its kind: only a private conversation whose person holds the owner role (`IDENTITY.tierOf`) gets it. A member's private conversation and any shared conversation, an owner's included, get none, and a failed or absent role lookup offers none. A host that ran its owner's conversation with kind `owner` but without `conversation: { visibility: "private", principalId }`, or without recording it private, loses the list until it does. Needs pi-roundtable 0.9, which gives `SessionContext.conversation`.
|
|
12
|
+
- `repo_task` refuses the host's own turns, whose speaker is `SYSTEM_PRINCIPAL`, such as an ops error report's or a Discord webhook report's: their text is untrusted input, and a coding task started there would run later as the owner's. Ask the owner to start it. Needs pi-roundtable 0.9, which exports `SYSTEM_PRINCIPAL`.
|
|
13
|
+
|
|
5
14
|
## [0.8.0] - 2026-10-07
|
|
6
15
|
|
|
7
16
|
- Release in lockstep with pi-roundtable 0.8.0; no package-specific behavior changes.
|
package/README.md
CHANGED
|
@@ -64,13 +64,20 @@ const workerPackages = [packageDir("your-provider-package", import.meta.url)];
|
|
|
64
64
|
Install the provider package on the host first and pass `workerPackages` to `coding`.
|
|
65
65
|
Workers activate only `SHELL_TOOLS` (`bash`, `read`, `edit`, `write`); extension packages are trusted executable code, not a way to grant extra model tools.
|
|
66
66
|
|
|
67
|
+
## Principal migration in 0.9
|
|
68
|
+
|
|
69
|
+
Use the same lockstep core and package release; [Migrating to 0.9](../../docs/migration-0.9.md) covers the host changes.
|
|
70
|
+
Owner skills depend on a private conversation's principal holding a lasting owner role, not `kind: "owner"` or an assumed tier.
|
|
71
|
+
Pass the router-resolved speaker to any custom turn or report integration, and direct personal reports only to that principal's own channel.
|
|
72
|
+
System report turns cannot call `repo_task`; do the work in the turn or hand it to a person.
|
|
73
|
+
|
|
67
74
|
## Tools and flow
|
|
68
75
|
|
|
69
76
|
| Tool | Parameters | Behavior |
|
|
70
77
|
| --- | --- | --- |
|
|
71
78
|
| `repo_add` | `repo` | Clone `owner/repo` into `shelfDir/owner/repo`; refuse invalid names and existing clones. |
|
|
72
79
|
| `repo_list` | `fetch?` | List paths, branches, upstream ahead/behind counts, dirty files, last commits, prose summaries, CI hints and linked skills. |
|
|
73
|
-
| `repo_task` | `repo`, `task`, `skills?` | Start a background worker, returning its job number immediately. |
|
|
80
|
+
| `repo_task` | `repo`, `task`, `skills?` | Start a background worker, returning its job number immediately. The host's own turns, such as a report's, may not. |
|
|
74
81
|
| `repo_change_report` | `repo` | Fetch and post the commits and diffstat ahead of the default branch; return its full SHA. |
|
|
75
82
|
| `repo_push` | `repo`, `sha` | Push the exact reported SHA to the reported default branch, without force, after the hold gate permits it. |
|
|
76
83
|
|
|
@@ -81,7 +88,11 @@ The host's skills addon resolves explicit skills, or the calling agent's carried
|
|
|
81
88
|
Missing or unknown skills refuse the task by default; requesting skills while the addon is off also refuses it.
|
|
82
89
|
`skipUnavailableCarriedSkills: true` opts into skipping unavailable implicit skills and disclosing them in the task-start text or through `presentation.taskStarted`.
|
|
83
90
|
Explicit skill requests still refuse missing or unknown skills.
|
|
84
|
-
|
|
91
|
+
A private conversation whose person holds the owner role, as `IDENTITY.tierOf` tells, gets the public `skillListExtension`, whatever its kind.
|
|
92
|
+
A member's private conversation and any shared conversation, an owner's included, get none; so does every conversation when the identity service is absent or its lookup fails.
|
|
93
|
+
Agent sessions keep the core's existing `skill_list` instead of registering it twice.
|
|
94
|
+
The role is checked when the session is built and again before every extra `skill_list` execution; a removed role or failed lookup immediately refuses the call, even in a reused session.
|
|
95
|
+
A newly granted role makes the extra tool available once the session is rebuilt.
|
|
85
96
|
|
|
86
97
|
A worker reads the repository's instructions, edits and checks the work, and commits using repository conventions.
|
|
87
98
|
One worker may use a repository at a time, with up to three per channel.
|
|
@@ -187,6 +198,8 @@ The `CODING` service exposes `shelf: RepoShelf` and `desk: CodingDesk` for trust
|
|
|
187
198
|
`RepoShelf`, `CodingDesk`, `PiCodingWorker`, `CodingWorkerFailure`, `REPO_TOOLS`, `codingReport`, `reportPost` and their option/result types are exported for testing and host integrations.
|
|
188
199
|
`resolveRun(turn)` receives the public `ToolTurn`, including the calling agent scope and channel.
|
|
189
200
|
It returns `{ model, thinking, channel, origin? }`; skill selection still follows the caller, not the resolved report destination.
|
|
201
|
+
A `resolveRun` or `postChangeReport` that sends a person's report away from the calling channel sends it to that person's own direct channel, `context.directChannels.reach(turn.speaker.principalId)`, and throws `ToolRefusal` when none reaches them.
|
|
202
|
+
It never falls back to the primary owner's channel: on a host serving more than one person, that channel would carry someone else's repository work.
|
|
190
203
|
`onResult` receives those fields plus loaded `skillNames` and the optional progress `thread` in `result.job`, so a host can enqueue its own localized conversation turn.
|
|
191
204
|
`adoptClones` and `RepoShelf.adopt` keep existing clone contents and path layout without database migrations.
|
|
192
205
|
No database or Discord-specific types are required.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-roundtable-coding",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Repository shelves and owner-approved Pi coding workers for pi-roundtable",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
"typebox": "1.3.34"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"pi-roundtable": ">=0.
|
|
41
|
+
"pi-roundtable": ">=0.9.0 <0.10.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@earendil-works/pi-ai": ">=1.0.0 <2",
|
|
45
45
|
"@biomejs/biome": "2.5.15",
|
|
46
46
|
"@types/bun": "1.4.2",
|
|
47
|
-
"pi-roundtable": "0.
|
|
47
|
+
"pi-roundtable": "0.9.1",
|
|
48
48
|
"typescript": "7.0.2"
|
|
49
49
|
}
|
|
50
50
|
}
|
package/src/coding-plugin.ts
CHANGED
|
@@ -3,8 +3,10 @@ import {
|
|
|
3
3
|
definePlugin,
|
|
4
4
|
defineTool,
|
|
5
5
|
type HoldCheck,
|
|
6
|
+
IDENTITY,
|
|
6
7
|
PluginError,
|
|
7
8
|
SKILLS,
|
|
9
|
+
SYSTEM_PRINCIPAL,
|
|
8
10
|
serviceKey,
|
|
9
11
|
type ThinkingLevel,
|
|
10
12
|
ToolRefusal,
|
|
@@ -213,6 +215,23 @@ export function coding(options: CodingOptions) {
|
|
|
213
215
|
}
|
|
214
216
|
}
|
|
215
217
|
context.services.provide(CODING, { shelf, desk });
|
|
218
|
+
// Whether the principal holds the owner role; no identity service, or a failed lookup,
|
|
219
|
+
// tells of no one holding it.
|
|
220
|
+
const holdsOwnerRole = async (
|
|
221
|
+
principalId: string,
|
|
222
|
+
session: { homeChannel: ChannelKey },
|
|
223
|
+
): Promise<boolean> => {
|
|
224
|
+
try {
|
|
225
|
+
const identity = context.services.find(IDENTITY);
|
|
226
|
+
return (await identity?.tierOf(principalId)) === "owner";
|
|
227
|
+
} catch (error) {
|
|
228
|
+
context.logger.warn(
|
|
229
|
+
{ channel: session.homeChannel, err: error },
|
|
230
|
+
"could not tell whether the conversation's person holds the owner role; no extra skill list",
|
|
231
|
+
);
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
216
235
|
const textOf = (tool: RepoToolName) => options.toolText?.[tool];
|
|
217
236
|
const describe = (tool: RepoToolName, fallback: string) =>
|
|
218
237
|
textOf(tool)?.description ?? fallback;
|
|
@@ -239,8 +258,45 @@ export function coding(options: CodingOptions) {
|
|
|
239
258
|
snapshot: () => ({
|
|
240
259
|
revision: 0,
|
|
241
260
|
factory: (session) => {
|
|
242
|
-
|
|
243
|
-
|
|
261
|
+
// Whom the conversation serves decides, not its persona's kind: an owner's own
|
|
262
|
+
// conversation. An agent's keeps the core's skill_list, and a shared one
|
|
263
|
+
// serves whoever speaks.
|
|
264
|
+
const { conversation } = session;
|
|
265
|
+
if (session.agent || conversation.visibility !== "private")
|
|
266
|
+
return null;
|
|
267
|
+
const list = skillListExtension(skills);
|
|
268
|
+
return async (pi) => {
|
|
269
|
+
if (
|
|
270
|
+
!(await holdsOwnerRole(
|
|
271
|
+
conversation.principalId,
|
|
272
|
+
session,
|
|
273
|
+
))
|
|
274
|
+
)
|
|
275
|
+
return;
|
|
276
|
+
await list({
|
|
277
|
+
...pi,
|
|
278
|
+
registerTool: (tool) =>
|
|
279
|
+
pi.registerTool({
|
|
280
|
+
...tool,
|
|
281
|
+
execute: async (...args) => {
|
|
282
|
+
// A cached session must not retain a removed owner role.
|
|
283
|
+
const current = session.conversation;
|
|
284
|
+
if (
|
|
285
|
+
session.agent ||
|
|
286
|
+
current.visibility !== "private" ||
|
|
287
|
+
!(await holdsOwnerRole(
|
|
288
|
+
current.principalId,
|
|
289
|
+
session,
|
|
290
|
+
))
|
|
291
|
+
)
|
|
292
|
+
throw new ToolRefusal(
|
|
293
|
+
"The extra skill list requires a private conversation whose principal currently holds the owner role.",
|
|
294
|
+
);
|
|
295
|
+
return tool.execute(...args);
|
|
296
|
+
},
|
|
297
|
+
}),
|
|
298
|
+
});
|
|
299
|
+
};
|
|
244
300
|
},
|
|
245
301
|
}),
|
|
246
302
|
},
|
|
@@ -368,6 +424,11 @@ export function coding(options: CodingOptions) {
|
|
|
368
424
|
}),
|
|
369
425
|
run: ({ repo, task, skills: names }, turn) =>
|
|
370
426
|
refusal(async () => {
|
|
427
|
+
// A report's turn reads text no one vouches for; it hands no worker a task.
|
|
428
|
+
if (turn.speaker?.principalId === SYSTEM_PRINCIPAL)
|
|
429
|
+
throw new AgentError(
|
|
430
|
+
"the host's own turns, such as a report's, start no coding tasks; ask the owner to start it",
|
|
431
|
+
);
|
|
371
432
|
if (shipping.has(repo))
|
|
372
433
|
throw new AgentError(
|
|
373
434
|
"A report or push is in progress for this repository.",
|