@sjawhar/pi-legion-envoy 1.40.2 → 1.41.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
CHANGED
|
@@ -85,10 +85,10 @@ extension files it does not contain.
|
|
|
85
85
|
|
|
86
86
|
## Native Dispatch tools
|
|
87
87
|
|
|
88
|
-
The extension registers
|
|
88
|
+
The extension registers seventeen native Dispatch tools: `dispatch_issue`, `dispatch_ask`, `dispatch_edit_ask`,
|
|
89
89
|
`dispatch_resolve_ask`, `dispatch_resolve_comment`, `dispatch_follow`, `dispatch_comment`, `dispatch_suggest`,
|
|
90
90
|
`dispatch_message`, `dispatch_doc_edit`, `dispatch_doc_read`, `dispatch_request_approval`, `dispatch_artifact`,
|
|
91
|
-
`dispatch_read`, `dispatch_search`, and `
|
|
91
|
+
`dispatch_read`, `dispatch_search`, `dispatch_open_asks`, and `dispatch_whoami`, when Dispatch configuration resolves both a base URL and bearer token.
|
|
92
92
|
|
|
93
93
|
Configure the shared `envoy.json` with:
|
|
94
94
|
|
|
@@ -112,7 +112,7 @@ whose trimmed contents are the token — how the Legion daemon delivers it to a
|
|
|
112
112
|
pane) wins over every other token source and never falls back when unreadable.
|
|
113
113
|
Omitting `dispatch.serverUrl` while `dispatch.enabled` is true targets
|
|
114
114
|
`http://localhost:8766`, the Go server's listen address. Invalid configuration,
|
|
115
|
-
an invalid URL, or an empty token leaves the
|
|
115
|
+
an invalid URL, or an empty token leaves the seventeen tools unavailable and
|
|
116
116
|
reports the source of the error.
|
|
117
117
|
|
|
118
118
|
Owner-scoped calls use either an issue (a native `KEY` or external `owner/repo#n` reference) or
|
package/dist/envoy.js
CHANGED
|
@@ -29889,7 +29889,8 @@ var dispatchToolSpecs = [
|
|
|
29889
29889
|
force: z.boolean().describe("Create even though POSSIBLE_DUPLICATE listed similar issues; pass it only after reading them.").optional(),
|
|
29890
29890
|
spec: z.string().describe(`Optional initial primary-document markdown. ${SPEC_WRITING_GUIDANCE}`).optional(),
|
|
29891
29891
|
labels: z.array(z.string({ min: 1, max: 40 }), { max: 20 }).describe("Optional initial labels, at most 20 labels of up to 40 characters.").optional(),
|
|
29892
|
-
priority: z.number({ int: true, min: 0, max: 3 }).describe("Optional coarse priority: P0 is highest and P3 is lowest.").optional()
|
|
29892
|
+
priority: z.number({ int: true, min: 0, max: 3 }).describe("Optional coarse priority: P0 is highest and P3 is lowest.").optional(),
|
|
29893
|
+
assignee: z.string().describe("GitHub login of the human who answers this issue's asks; defaults to your owner when you act for a person, else the parent's assignee, else unassigned.").optional()
|
|
29893
29894
|
})
|
|
29894
29895
|
},
|
|
29895
29896
|
{
|
|
@@ -30094,6 +30095,12 @@ var dispatchToolSpecs = [
|
|
|
30094
30095
|
description: "List this session's active unanswered asks across issues and project documents, including age and whose reply is needed. Call before saying you are waiting for human input.",
|
|
30095
30096
|
arguments: () => ({}),
|
|
30096
30097
|
strict: true
|
|
30098
|
+
},
|
|
30099
|
+
{
|
|
30100
|
+
name: "dispatch_whoami",
|
|
30101
|
+
description: "Who Dispatch takes this session for: {session, owner}. owner is the lowercase GitHub login of the human whose personal token you run under (the default assignee of issues you create), or null under the shared token.",
|
|
30102
|
+
arguments: () => ({}),
|
|
30103
|
+
strict: true
|
|
30097
30104
|
}
|
|
30098
30105
|
];
|
|
30099
30106
|
// ../contracts/src/envelope.ts
|
|
@@ -31873,6 +31880,9 @@ class DispatchClient {
|
|
|
31873
31880
|
...since === undefined ? {} : { since }
|
|
31874
31881
|
});
|
|
31875
31882
|
}
|
|
31883
|
+
async whoami() {
|
|
31884
|
+
return this.#json("GET", ["api", "v1", "whoami"]);
|
|
31885
|
+
}
|
|
31876
31886
|
async resolveAsk(id, input) {
|
|
31877
31887
|
return this.#json("POST", ["api", "v1", "asks", id, "resolve"], input);
|
|
31878
31888
|
}
|
|
@@ -32286,7 +32296,8 @@ var issueFreeTools = {
|
|
|
32286
32296
|
dispatch_resolve_comment: true,
|
|
32287
32297
|
dispatch_follow: true,
|
|
32288
32298
|
dispatch_search: true,
|
|
32289
|
-
dispatch_open_asks: true
|
|
32299
|
+
dispatch_open_asks: true,
|
|
32300
|
+
dispatch_whoami: true
|
|
32290
32301
|
};
|
|
32291
32302
|
function canonicalExternalIssueRef(value) {
|
|
32292
32303
|
const match = value.trim().match(externalIssueRefPattern);
|
|
@@ -32702,10 +32713,13 @@ function issueSummary(issue, events, references, graph) {
|
|
|
32702
32713
|
const specApproval = spec === undefined ? undefined : approvalLine(spec);
|
|
32703
32714
|
if (issue.priority === undefined)
|
|
32704
32715
|
throw new Error("Dispatch issue is missing priority");
|
|
32716
|
+
if (issue.assignee === undefined)
|
|
32717
|
+
throw new Error("Dispatch issue is missing assignee");
|
|
32705
32718
|
return [
|
|
32706
32719
|
`Title: ${issue.title}`,
|
|
32707
32720
|
`Key: ${issue.key}`,
|
|
32708
32721
|
`Status: ${issue.status}`,
|
|
32722
|
+
`Assignee: ${issue.assignee ?? "unassigned"}`,
|
|
32709
32723
|
...issue.priority === null ? [] : [`Priority: P${issue.priority}`],
|
|
32710
32724
|
`Labels: ${issue.labels.length === 0 ? "none" : issue.labels.join(", ")}`,
|
|
32711
32725
|
`Route: ${issue.route ?? "none"}`,
|
|
@@ -32953,6 +32967,18 @@ async function executeDispatchTool(input) {
|
|
|
32953
32967
|
const response = await client.openAsks(sessionId);
|
|
32954
32968
|
return { text: formatOpenAsksSummary(response, configUrl), details: { ...response } };
|
|
32955
32969
|
}
|
|
32970
|
+
if (input.tool === "dispatch_whoami") {
|
|
32971
|
+
const sessionId = input.sessionId?.trim();
|
|
32972
|
+
if (!sessionId)
|
|
32973
|
+
throw new Error("host session id is required for dispatch_whoami");
|
|
32974
|
+
const client = new DispatchClient(configUrl, configToken, fetchImpl, input.signal);
|
|
32975
|
+
const identity = await client.whoami();
|
|
32976
|
+
const owner = identity.kind === "agent" ? identity.owner : identity.login.toLowerCase();
|
|
32977
|
+
return {
|
|
32978
|
+
text: owner === null ? `Session ${sessionId} runs under the shared token: no owner, so issues you create without an assignee are unassigned (or inherit their parent's).` : `Session ${sessionId} acts for ${owner}: issues you create without an assignee are assigned to ${owner}.`,
|
|
32979
|
+
details: { session: sessionId, owner }
|
|
32980
|
+
};
|
|
32981
|
+
}
|
|
32956
32982
|
const args = parsed.success ? parsed.data : ownerArguments.args;
|
|
32957
32983
|
const actor = toolActor(await resolveOrigin(env, exec, input.cwd), input);
|
|
32958
32984
|
const client = new DispatchClient(configUrl, configToken, fetchImpl, input.signal);
|
|
@@ -32979,6 +33005,7 @@ async function executeDispatchTool(input) {
|
|
|
32979
33005
|
const force = optionalBoolean(args, "force");
|
|
32980
33006
|
const spec = optionalString(args, "spec");
|
|
32981
33007
|
const priority = optionalNumber(args, "priority");
|
|
33008
|
+
const assignee = optionalString(args, "assignee");
|
|
32982
33009
|
const labels = args.labels;
|
|
32983
33010
|
try {
|
|
32984
33011
|
const created = await client.issue({
|
|
@@ -32989,6 +33016,7 @@ async function executeDispatchTool(input) {
|
|
|
32989
33016
|
...force === undefined ? {} : { force },
|
|
32990
33017
|
...spec === undefined ? {} : { spec },
|
|
32991
33018
|
...priority === undefined ? {} : { priority },
|
|
33019
|
+
...assignee === undefined ? {} : { assignee },
|
|
32992
33020
|
...Array.isArray(labels) ? { labels } : {},
|
|
32993
33021
|
actor
|
|
32994
33022
|
});
|
package/dist/legion.js
CHANGED
|
@@ -29163,7 +29163,8 @@ var dispatchToolSpecs = [
|
|
|
29163
29163
|
force: z.boolean().describe("Create even though POSSIBLE_DUPLICATE listed similar issues; pass it only after reading them.").optional(),
|
|
29164
29164
|
spec: z.string().describe(`Optional initial primary-document markdown. ${SPEC_WRITING_GUIDANCE}`).optional(),
|
|
29165
29165
|
labels: z.array(z.string({ min: 1, max: 40 }), { max: 20 }).describe("Optional initial labels, at most 20 labels of up to 40 characters.").optional(),
|
|
29166
|
-
priority: z.number({ int: true, min: 0, max: 3 }).describe("Optional coarse priority: P0 is highest and P3 is lowest.").optional()
|
|
29166
|
+
priority: z.number({ int: true, min: 0, max: 3 }).describe("Optional coarse priority: P0 is highest and P3 is lowest.").optional(),
|
|
29167
|
+
assignee: z.string().describe("GitHub login of the human who answers this issue's asks; defaults to your owner when you act for a person, else the parent's assignee, else unassigned.").optional()
|
|
29167
29168
|
})
|
|
29168
29169
|
},
|
|
29169
29170
|
{
|
|
@@ -29368,6 +29369,12 @@ var dispatchToolSpecs = [
|
|
|
29368
29369
|
description: "List this session's active unanswered asks across issues and project documents, including age and whose reply is needed. Call before saying you are waiting for human input.",
|
|
29369
29370
|
arguments: () => ({}),
|
|
29370
29371
|
strict: true
|
|
29372
|
+
},
|
|
29373
|
+
{
|
|
29374
|
+
name: "dispatch_whoami",
|
|
29375
|
+
description: "Who Dispatch takes this session for: {session, owner}. owner is the lowercase GitHub login of the human whose personal token you run under (the default assignee of issues you create), or null under the shared token.",
|
|
29376
|
+
arguments: () => ({}),
|
|
29377
|
+
strict: true
|
|
29371
29378
|
}
|
|
29372
29379
|
];
|
|
29373
29380
|
// ../contracts/src/envelope.ts
|
|
@@ -90,13 +90,21 @@ exactly one owner to every owner-scoped tool: `issue` for an issue, or `project`
|
|
|
90
90
|
[References](#references) for the resulting ref shape). On first use, an external issue reference creates its native issue in the
|
|
91
91
|
project configured for that repository in Dispatch Settings, then falls back to `DISPATCH_DEFAULT_PROJECT`.
|
|
92
92
|
|
|
93
|
-
Issue reads include `rank`, the server-owned ordering key used by project boards; reorder through `PATCH /api/v1/issues/{key}` with neighboring issue keys. They also include nullable coarse priority (`P0` highest through `P3` lowest).
|
|
93
|
+
Issue reads include `rank`, the server-owned ordering key used by project boards; reorder through `PATCH /api/v1/issues/{key}` with neighboring issue keys. They also include nullable coarse priority (`P0` highest through `P3` lowest) and `assignee`: the lowercase GitHub login of the human who answers the issue's asks, or `null` when nobody holds it. `dispatch_read` of an issue prints it as `Assignee: <login>` or `Assignee: unassigned`.
|
|
94
|
+
|
|
95
|
+
### Who answers an ask
|
|
96
|
+
|
|
97
|
+
An ask goes to the issue's assignee: their Inbox opens on **Mine**, which lists asks on the issues they hold plus an Unassigned band; an ask on an unassigned issue waits in that band for someone to take it. Find out who Dispatch takes you for with:
|
|
98
|
+
```ts
|
|
99
|
+
dispatch_whoami({})
|
|
100
|
+
```
|
|
101
|
+
It returns `details` `{ session, owner }`: `owner` is the lowercase login of the human whose personal token you run under, or `null` under the shared token. An issue you create without `assignee` goes to your owner; under the shared token it inherits its parent's assignee, or stays unassigned without a parent. If an issue you are asking on is unassigned and the answer matters, assign it to your owner (`PATCH /api/v1/issues/{key}` with `{"assignee": "<login>"}`; any authenticated caller may reassign, and an unlisted login is refused with `ASSIGNEE_NOT_ALLOWED`) or name in the question who should answer it. Never reassign an issue a human holds to get an answer faster: that is the human's call.
|
|
94
102
|
|
|
95
103
|
Architects create newly tracked child work with:
|
|
96
104
|
```ts
|
|
97
|
-
dispatch_issue({ project, title, parent?, external?, spec?, force?, labels?: string[], priority?: 0 | 1 | 2 | 3 })
|
|
105
|
+
dispatch_issue({ project, title, parent?, external?, spec?, force?, labels?: string[], priority?: 0 | 1 | 2 | 3, assignee?: string })
|
|
98
106
|
```
|
|
99
|
-
`labels` are optional initial labels: Dispatch trims them, preserves their case, and removes case-insensitive duplicates. Set `priority` on creation only when the human's intent makes the bucket clear; otherwise priority remains the human's decision. It returns
|
|
107
|
+
`labels` are optional initial labels: Dispatch trims them, preserves their case, and removes case-insensitive duplicates. Set `priority` on creation only when the human's intent makes the bucket clear; otherwise priority remains the human's decision. Set `assignee` (a GitHub login on the sign-in allowlist) only when the human said who owns the work; otherwise the default above applies, so a child inherits its parent's assignee. It returns
|
|
100
108
|
`details` `{ issue }`; creating an issue does not subscribe you to it (see [Following](#following)). Use `dispatch_issue` only to create an issue; never use it to park a question. When `spec` is supplied,
|
|
101
109
|
follow [Writing a spec](#writing-a-spec).
|
|
102
110
|
|
|
@@ -75,7 +75,10 @@ exercise a criterion end to end, building that path is a child issue of this tre
|
|
|
75
75
|
the role-token `<project>` (the daemon's own project, e.g. `acme`), a different string. A
|
|
76
76
|
root session has `LEGION_TREE == LEGION_ISSUE`. The daemon establishes the sub-issue
|
|
77
77
|
relationship from `parent`. Keep the returned issue keys in ordered waves; a child is
|
|
78
|
-
inert until released.
|
|
78
|
+
inert until released. Do not pass `assignee`: the default keeps the tree's questions in one
|
|
79
|
+
Inbox — under the shared token a child inherits its parent's assignee (the human who
|
|
80
|
+
answers the tree's asks); under a personal token it goes to that token's owner, whom
|
|
81
|
+
`dispatch_whoami` names. Set it only when a human told you a specific person owns that child.
|
|
79
82
|
|
|
80
83
|
Specifications written into Dispatch follow [`skills/dispatch`'s Writing a spec](../dispatch/SKILL.md#writing-a-spec).
|
|
81
84
|
Wave releases, child closures, and your own status are visible from the issue tree and the
|
|
@@ -107,6 +107,9 @@ override a Sami ruling quoted here.
|
|
|
107
107
|
1. Read `legion state --json`, then inspect the reported Dispatch issue with `dispatch_read`.
|
|
108
108
|
Verify the issue is in this project, is eligible for a root process, and whether it
|
|
109
109
|
has pre-existing children. Dispatch and daemon state, not the wake text, decide triage.
|
|
110
|
+
Note the `Assignee:` line: that human answers the tree's asks, and their Inbox opens on
|
|
111
|
+
the issues they hold. Never reassign during triage — who holds an issue is the humans'
|
|
112
|
+
decision, made from the issue header.
|
|
110
113
|
2. If it should run now, admit the root issue:
|
|
111
114
|
|
|
112
115
|
```text
|
|
@@ -123,6 +126,11 @@ override a Sami ruling quoted here.
|
|
|
123
126
|
(or `icebox` for longer-term deferral). Dispatch status is the durable record;
|
|
124
127
|
there is no separate marker to maintain. Do not triage a system-created child as a root
|
|
125
128
|
issue.
|
|
129
|
+
4. When you post a triage note (a `dispatch_comment` on the issue saying what you decided and
|
|
130
|
+
why), name who will be asked: `Assigned to <login>, who will get this tree's questions`,
|
|
131
|
+
or, when the `Assignee:` line says `unassigned`, `Unassigned — nobody's Inbox shows this
|
|
132
|
+
tree's questions until someone takes it from the issue header (Assignee, beside Priority)`.
|
|
133
|
+
An unassigned root still runs; the architect's asks wait in every Inbox's Unassigned band.
|
|
126
134
|
|
|
127
135
|
## Backlog eligibility
|
|
128
136
|
|