@sjawhar/pi-envoy 10.18.0 → 10.20.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/dist/dispatch.js
CHANGED
|
@@ -13948,9 +13948,10 @@ function documentOwnerValidation(requireArtifact, alwaysRequireArtifact = false)
|
|
|
13948
13948
|
message: alwaysRequireArtifact ? "Exactly one of issue and project is required; artifact or ref must name the document." : "Exactly one of issue and project is required; with project, artifact names the document."
|
|
13949
13949
|
};
|
|
13950
13950
|
}
|
|
13951
|
+
var documentEditOwner = documentOwnerValidation(true);
|
|
13951
13952
|
var documentEditValidation = {
|
|
13952
13953
|
check: (value) => {
|
|
13953
|
-
if (!
|
|
13954
|
+
if (!documentEditOwner.check(value))
|
|
13954
13955
|
return false;
|
|
13955
13956
|
const input = value;
|
|
13956
13957
|
if (input.precondition === undefined)
|
|
@@ -13960,7 +13961,7 @@ var documentEditValidation = {
|
|
|
13960
13961
|
const precondition = input.precondition;
|
|
13961
13962
|
return typeof precondition.document === "string" !== Array.isArray(precondition.blocks);
|
|
13962
13963
|
},
|
|
13963
|
-
message:
|
|
13964
|
+
message: `${documentEditOwner.message} A precondition selects exactly one of document or blocks.`
|
|
13964
13965
|
};
|
|
13965
13966
|
var commentOwner = documentOwnerValidation(true);
|
|
13966
13967
|
var commentValidation = {
|
|
@@ -14060,7 +14061,7 @@ var dispatchToolSpecs = [
|
|
|
14060
14061
|
spec: z2.string().describe(`Optional initial primary-document markdown. ${SPEC_WRITING_POINTER}`).optional(),
|
|
14061
14062
|
labels: z2.array(z2.string({ min: 1, max: 40 }), { max: 20 }).describe("Optional initial labels, at most 20 labels of up to 40 characters.").optional(),
|
|
14062
14063
|
priority: z2.number({ int: true, min: 0, max: 3 }).describe("Optional coarse priority, a number from 0 to 3: 0 is highest and 3 is lowest.").optional(),
|
|
14063
|
-
assignee: z2.string().describe("Email of the person who answers this issue's asks (someone who has signed in to Dispatch); defaults to your owner when you act for a person, else the parent's assignee, else unassigned.").optional(),
|
|
14064
|
+
assignee: z2.string().describe("Email of the person who answers this issue's asks (someone who has signed in to Dispatch); defaults to your owner when you act for a person, else the parent's assignee, else the project's default assignee when it still names a person who has signed in, else unassigned.").optional(),
|
|
14064
14065
|
components: componentsArgument(z2).optional(),
|
|
14065
14066
|
repo: z2.string().describe("The repository the issue targets, owner/name, mapped to the issue's project in Settings; a child inherits its parent's when unset, and an issue with none on its parent chain takes the project's default.").optional()
|
|
14066
14067
|
})
|
|
@@ -14099,7 +14100,7 @@ var dispatchToolSpecs = [
|
|
|
14099
14100
|
{
|
|
14100
14101
|
name: "dispatch_claim",
|
|
14101
14102
|
example: { issue: "DSP-1" },
|
|
14102
|
-
description: "Claim a Dispatch issue before you start implementing it, so no other session takes the same work, " + "and release it when you stop. Pass the issue alone to claim it, or --release to give it up. " + "Your claim records your own session and shows on every read of the issue: the dashboard header, the " + "issue list and board, dispatch read, and dispatch issues. Claiming is refused with 409 ISSUE_CLAIMED " + "when another session holds the issue and is still running; the refusal names that session, so talk to " + "it instead of working the same issue in parallel. When a human holds the claim the refusal names the " + "person, not a session: there is nothing running to message, so ask them on the issue rather than " + "taking it. 409 CLAIM_CONTENDED means the issue changed " + "hands twice while your call ran, so nothing was applied and nobody's liveness was checked: read " + "the issue and decide again. A claim whose session is no longer running may be " + "taken: the takeover is recorded on the issue and the session that lost it is told. A claim is not the " + "issue's status \u2014 claiming moves nothing,
|
|
14103
|
+
description: "Claim a Dispatch issue before you start implementing it, so no other session takes the same work, " + "and release it when you stop. Pass the issue alone to claim it, or --release to give it up. " + "Your claim records your own session and shows on every read of the issue: the dashboard header, the " + "issue list and board, dispatch read, and dispatch issues. Claiming is refused with 409 ISSUE_CLAIMED " + "when another session holds the issue and is still running; the refusal names that session, so talk to " + "it instead of working the same issue in parallel. When a human holds the claim the refusal names the " + "person, not a session: there is nothing running to message, so ask them on the issue rather than " + "taking it. 409 CLAIM_CONTENDED means the issue changed " + "hands twice while your call ran, so nothing was applied and nobody's liveness was checked: read " + "the issue and decide again. A claim whose session is no longer running may be " + "taken: the takeover is recorded on the issue and the session that lost it is told. A claim is not the " + "issue's status \u2014 claiming moves nothing, and the result names who moves the status: you, with " + "dispatch issue-update, or Legion's daemon on an issue it runs, where a status written by anyone " + "else is undone. A claim is released by its holder or any human, and by " + "any agent once the holder's session is no longer running. " + ISSUE_REFERENCE,
|
|
14103
14104
|
arguments: (z2) => ({
|
|
14104
14105
|
issue: z2.string().describe(ISSUE_REFERENCE),
|
|
14105
14106
|
release: z2.boolean().describe("Give up your claim instead of taking it; the issue's status does not change.").optional()
|
|
@@ -14276,7 +14277,7 @@ var dispatchToolSpecs = [
|
|
|
14276
14277
|
arguments: (z2) => ({
|
|
14277
14278
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
14278
14279
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
14279
|
-
artifact: z2.string().describe("
|
|
14280
|
+
artifact: z2.string().describe("Optional project document artifact id, slug, or filename; primary document by default for an issue.").optional(),
|
|
14280
14281
|
ref: z2.string().describe("Optional dispatch:// issue or document reference.").optional(),
|
|
14281
14282
|
ops: z2.array(z2.object({
|
|
14282
14283
|
op: z2.enum(DOC_EDIT_OPS).describe("Edit operation."),
|
|
@@ -14438,7 +14439,7 @@ var dispatchToolSpecs = [
|
|
|
14438
14439
|
{
|
|
14439
14440
|
name: "dispatch_whoami",
|
|
14440
14441
|
example: {},
|
|
14441
|
-
description: "Who Dispatch takes this session for: {session, owner}. owner is the lowercase email of the person whose personal token you run under (the
|
|
14442
|
+
description: "Who Dispatch takes this session for: {session, owner}. owner is the lowercase email of the person whose personal token you run under (the first choice of assignee for issues you create), or null under the shared token, in which case a create with no assignee falls back to the parent's assignee and then to the project's default assignee.",
|
|
14442
14443
|
arguments: () => ({}),
|
|
14443
14444
|
strict: true
|
|
14444
14445
|
}
|
|
@@ -16263,7 +16264,7 @@ function renderAdvice(tool, key, advice, opts) {
|
|
|
16263
16264
|
if (advice.decision_blocks === 0 && opts.isPrimarySpec === true && (tool === "dispatch_issue" || tool === "dispatch_artifact")) {
|
|
16264
16265
|
lines.push('This spec holds no ask blocks, so nothing here reaches a human\'s inbox. Want human feedback? See the `dispatch` skill, "Decision blocks".');
|
|
16265
16266
|
}
|
|
16266
|
-
if (advice.issue_status === "triage" && tool !== "dispatch_issue" && !(tool === "dispatch_issue_update" && opts.setsStatus === true) && !triageAdviceShown.has(key)) {
|
|
16267
|
+
if (advice.issue_status === "triage" && advice.legion_managed_status !== true && tool !== "dispatch_issue" && !(tool === "dispatch_issue_update" && opts.setsStatus === true) && !triageAdviceShown.has(key)) {
|
|
16267
16268
|
triageAdviceShown.add(key);
|
|
16268
16269
|
lines.push(`${key} is still in triage \u2014 nobody can see its development status. See the \`dispatch\` skill, "Issue status is yours to move".`);
|
|
16269
16270
|
}
|
|
@@ -16420,6 +16421,7 @@ function componentsChange(input, after) {
|
|
|
16420
16421
|
}
|
|
16421
16422
|
}
|
|
16422
16423
|
var architectureComponentsAction = 'Review the `dispatch` skill, "Architecture components", to attach it to the parts it changes or mark it as non-architectural with a reason.';
|
|
16424
|
+
var LEGION_OWNS_STATUS = "Legion's daemon moves this issue's status on an issue it runs, so leave it alone \u2014 a status written here is undone.";
|
|
16423
16425
|
async function architectureGuidance(client, project, components) {
|
|
16424
16426
|
if (components.mode === "none" || components.ids.length > 0)
|
|
16425
16427
|
return;
|
|
@@ -16737,8 +16739,8 @@ function innerRefProblem(tool, ref, refText) {
|
|
|
16737
16739
|
if (tool === "dispatch_read" || tool === "dispatch_ask")
|
|
16738
16740
|
return;
|
|
16739
16741
|
const owner = refOwnerName(ref);
|
|
16740
|
-
const
|
|
16741
|
-
const ownerArguments = ref.owner.kind === "issue" ? `issue ${ref.owner.issue}${
|
|
16742
|
+
const suggestTool = tool === "dispatch_suggest";
|
|
16743
|
+
const ownerArguments = ref.owner.kind === "issue" ? `issue ${ref.owner.issue}${suggestTool ? " and artifact <the document's slug>" : ""}` : `project ${ref.owner.project} and artifact ${ref.artifact}`;
|
|
16742
16744
|
const fix = tool === "dispatch_comment" && ref.kind === "ask" ? `reply under it with reply_to_ask ${ref.id} and ${ownerArguments}` : tool === "dispatch_comment" && ref.kind === "comment" ? `reply in its thread with reply_to ${ref.id} and ${ownerArguments}` : `read it with dispatch read, or name ${owner} with ${ownerArguments}`;
|
|
16743
16745
|
const item = ref.kind === "ask" ? "an ask" : ref.kind === "log" ? "the event log" : ref.kind === "children" ? "the children" : `a ${ref.kind}`;
|
|
16744
16746
|
return `ref ${refText} names ${item} on ${owner}; dispatch ${commandName(tool)} would act on ${owner} and drop the ${ref.kind}: ${fix}`;
|
|
@@ -17683,7 +17685,7 @@ async function executeDispatchTool(input) {
|
|
|
17683
17685
|
const identity = await client2.whoami();
|
|
17684
17686
|
const owner2 = identity.kind === "agent" ? identity.owner : identity.login.toLowerCase();
|
|
17685
17687
|
const service = identity.kind === "agent" ? identity.service ?? null : null;
|
|
17686
|
-
const unowned = "no owner, so
|
|
17688
|
+
const unowned = "no owner, so an issue you create without an assignee takes its parent's assignee, else the project's default assignee, else nobody.";
|
|
17687
17689
|
return {
|
|
17688
17690
|
text: owner2 !== null ? `Session ${sessionId} acts for ${owner2}: issues you create without an assignee are assigned to ${owner2}.` : service !== null ? `Session ${sessionId} runs as service ${serviceSubjectLabel(service)}: ${unowned}` : `Session ${sessionId} runs under the shared token: ${unowned}`,
|
|
17689
17691
|
details: { session: sessionId, owner: owner2, service }
|
|
@@ -17924,14 +17926,17 @@ async function executeDispatchTool(input) {
|
|
|
17924
17926
|
throw refusalWithCode(error48);
|
|
17925
17927
|
}
|
|
17926
17928
|
const held = after.claim;
|
|
17929
|
+
const legionManaged = after.legion_managed_status === true;
|
|
17927
17930
|
let text;
|
|
17928
17931
|
if (release) {
|
|
17929
|
-
|
|
17932
|
+
const released = `${issueKey}: claim released; nobody is working it now. Its status is still ${after.status}`;
|
|
17933
|
+
text = legionManaged ? `${released}; ${LEGION_OWNS_STATUS}` : `${released} \u2014 move it yourself if that is no longer where the work is.`;
|
|
17930
17934
|
} else {
|
|
17931
17935
|
if (held === null) {
|
|
17932
17936
|
throw new Error(`Dispatch claimed ${issueKey} but answered with no claim`);
|
|
17933
17937
|
}
|
|
17934
|
-
|
|
17938
|
+
const claimed = `${issueKey}: claimed by you since ${held.at}. Its status is ${after.status}`;
|
|
17939
|
+
text = legionManaged ? `${claimed}; ${LEGION_OWNS_STATUS} Release the claim when you stop.` : `${claimed}; a claim moves nothing, so move it to in_progress with dispatch issue-update when you start, and release the claim when you stop.`;
|
|
17935
17940
|
}
|
|
17936
17941
|
return {
|
|
17937
17942
|
text: `${text}${await notSubscribed(issueTopic(issueKey), input.subscriptions)}`,
|
|
@@ -18289,7 +18294,7 @@ ${await followsAsk(askOwner, input.subscriptions)}`,
|
|
|
18289
18294
|
};
|
|
18290
18295
|
}
|
|
18291
18296
|
case "dispatch_doc_edit": {
|
|
18292
|
-
const resolved = await resolveDocument(documentOwner(),
|
|
18297
|
+
const resolved = await resolveDocument(documentOwner(), optionalString(args, "artifact"));
|
|
18293
18298
|
const ops = args.ops;
|
|
18294
18299
|
const summary = optionalString(args, "summary");
|
|
18295
18300
|
const { precondition: rawPrecondition } = args;
|
package/dist/envoy.js
CHANGED
|
@@ -29935,9 +29935,10 @@ function documentOwnerValidation(requireArtifact, alwaysRequireArtifact = false)
|
|
|
29935
29935
|
message: alwaysRequireArtifact ? "Exactly one of issue and project is required; artifact or ref must name the document." : "Exactly one of issue and project is required; with project, artifact names the document."
|
|
29936
29936
|
};
|
|
29937
29937
|
}
|
|
29938
|
+
var documentEditOwner = documentOwnerValidation(true);
|
|
29938
29939
|
var documentEditValidation = {
|
|
29939
29940
|
check: (value) => {
|
|
29940
|
-
if (!
|
|
29941
|
+
if (!documentEditOwner.check(value))
|
|
29941
29942
|
return false;
|
|
29942
29943
|
const input = value;
|
|
29943
29944
|
if (input.precondition === undefined)
|
|
@@ -29947,7 +29948,7 @@ var documentEditValidation = {
|
|
|
29947
29948
|
const precondition = input.precondition;
|
|
29948
29949
|
return typeof precondition.document === "string" !== Array.isArray(precondition.blocks);
|
|
29949
29950
|
},
|
|
29950
|
-
message:
|
|
29951
|
+
message: `${documentEditOwner.message} A precondition selects exactly one of document or blocks.`
|
|
29951
29952
|
};
|
|
29952
29953
|
var commentOwner = documentOwnerValidation(true);
|
|
29953
29954
|
var commentValidation = {
|
|
@@ -30047,7 +30048,7 @@ var dispatchToolSpecs = [
|
|
|
30047
30048
|
spec: z2.string().describe(`Optional initial primary-document markdown. ${SPEC_WRITING_POINTER}`).optional(),
|
|
30048
30049
|
labels: z2.array(z2.string({ min: 1, max: 40 }), { max: 20 }).describe("Optional initial labels, at most 20 labels of up to 40 characters.").optional(),
|
|
30049
30050
|
priority: z2.number({ int: true, min: 0, max: 3 }).describe("Optional coarse priority, a number from 0 to 3: 0 is highest and 3 is lowest.").optional(),
|
|
30050
|
-
assignee: z2.string().describe("Email of the person who answers this issue's asks (someone who has signed in to Dispatch); defaults to your owner when you act for a person, else the parent's assignee, else unassigned.").optional(),
|
|
30051
|
+
assignee: z2.string().describe("Email of the person who answers this issue's asks (someone who has signed in to Dispatch); defaults to your owner when you act for a person, else the parent's assignee, else the project's default assignee when it still names a person who has signed in, else unassigned.").optional(),
|
|
30051
30052
|
components: componentsArgument(z2).optional(),
|
|
30052
30053
|
repo: z2.string().describe("The repository the issue targets, owner/name, mapped to the issue's project in Settings; a child inherits its parent's when unset, and an issue with none on its parent chain takes the project's default.").optional()
|
|
30053
30054
|
})
|
|
@@ -30086,7 +30087,7 @@ var dispatchToolSpecs = [
|
|
|
30086
30087
|
{
|
|
30087
30088
|
name: "dispatch_claim",
|
|
30088
30089
|
example: { issue: "DSP-1" },
|
|
30089
|
-
description: "Claim a Dispatch issue before you start implementing it, so no other session takes the same work, " + "and release it when you stop. Pass the issue alone to claim it, or --release to give it up. " + "Your claim records your own session and shows on every read of the issue: the dashboard header, the " + "issue list and board, dispatch read, and dispatch issues. Claiming is refused with 409 ISSUE_CLAIMED " + "when another session holds the issue and is still running; the refusal names that session, so talk to " + "it instead of working the same issue in parallel. When a human holds the claim the refusal names the " + "person, not a session: there is nothing running to message, so ask them on the issue rather than " + "taking it. 409 CLAIM_CONTENDED means the issue changed " + "hands twice while your call ran, so nothing was applied and nobody's liveness was checked: read " + "the issue and decide again. A claim whose session is no longer running may be " + "taken: the takeover is recorded on the issue and the session that lost it is told. A claim is not the " + "issue's status \u2014 claiming moves nothing,
|
|
30090
|
+
description: "Claim a Dispatch issue before you start implementing it, so no other session takes the same work, " + "and release it when you stop. Pass the issue alone to claim it, or --release to give it up. " + "Your claim records your own session and shows on every read of the issue: the dashboard header, the " + "issue list and board, dispatch read, and dispatch issues. Claiming is refused with 409 ISSUE_CLAIMED " + "when another session holds the issue and is still running; the refusal names that session, so talk to " + "it instead of working the same issue in parallel. When a human holds the claim the refusal names the " + "person, not a session: there is nothing running to message, so ask them on the issue rather than " + "taking it. 409 CLAIM_CONTENDED means the issue changed " + "hands twice while your call ran, so nothing was applied and nobody's liveness was checked: read " + "the issue and decide again. A claim whose session is no longer running may be " + "taken: the takeover is recorded on the issue and the session that lost it is told. A claim is not the " + "issue's status \u2014 claiming moves nothing, and the result names who moves the status: you, with " + "dispatch issue-update, or Legion's daemon on an issue it runs, where a status written by anyone " + "else is undone. A claim is released by its holder or any human, and by " + "any agent once the holder's session is no longer running. " + ISSUE_REFERENCE,
|
|
30090
30091
|
arguments: (z2) => ({
|
|
30091
30092
|
issue: z2.string().describe(ISSUE_REFERENCE),
|
|
30092
30093
|
release: z2.boolean().describe("Give up your claim instead of taking it; the issue's status does not change.").optional()
|
|
@@ -30263,7 +30264,7 @@ var dispatchToolSpecs = [
|
|
|
30263
30264
|
arguments: (z2) => ({
|
|
30264
30265
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
30265
30266
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
30266
|
-
artifact: z2.string().describe("
|
|
30267
|
+
artifact: z2.string().describe("Optional project document artifact id, slug, or filename; primary document by default for an issue.").optional(),
|
|
30267
30268
|
ref: z2.string().describe("Optional dispatch:// issue or document reference.").optional(),
|
|
30268
30269
|
ops: z2.array(z2.object({
|
|
30269
30270
|
op: z2.enum(DOC_EDIT_OPS).describe("Edit operation."),
|
|
@@ -30425,7 +30426,7 @@ var dispatchToolSpecs = [
|
|
|
30425
30426
|
{
|
|
30426
30427
|
name: "dispatch_whoami",
|
|
30427
30428
|
example: {},
|
|
30428
|
-
description: "Who Dispatch takes this session for: {session, owner}. owner is the lowercase email of the person whose personal token you run under (the
|
|
30429
|
+
description: "Who Dispatch takes this session for: {session, owner}. owner is the lowercase email of the person whose personal token you run under (the first choice of assignee for issues you create), or null under the shared token, in which case a create with no assignee falls back to the parent's assignee and then to the project's default assignee.",
|
|
30429
30430
|
arguments: () => ({}),
|
|
30430
30431
|
strict: true
|
|
30431
30432
|
}
|
|
@@ -4,8 +4,8 @@ Read this once an ask is open. Whether to ask, and how to write it, is "Before y
|
|
|
4
4
|
|
|
5
5
|
## Who answers an ask
|
|
6
6
|
|
|
7
|
-
- An ask goes to the issue's assignee's Inbox. On an unassigned issue it is in nobody's default Inbox or `Needs you` count.
|
|
8
|
-
- `dispatch whoami` prints whom you act for: your token owner's email, or no owner (a service token shows as `<namespace>/<name>`, as your writes do). An issue you create goes to your owner, else inherits its parent's assignee, else stays unassigned.
|
|
7
|
+
- An ask goes to the issue's assignee's Inbox: the `Assignee:` that `dispatch read` prints decides whose, not the project's default. On an unassigned issue it is in nobody's default Inbox or `Needs you` count. A project's default assignee fills in only when an issue is created with nobody named and nothing earlier in the chain below names someone; an issue created with an explicit nobody, set to nobody later, or created before the default was set stays unassigned.
|
|
8
|
+
- `dispatch whoami` prints whom you act for: your token owner's email, or no owner (a service token shows as `<namespace>/<name>`, as your writes do). An issue you create goes to your owner, else inherits its parent's assignee, else goes to the project's default assignee when a person set one on the Settings page, else stays unassigned.
|
|
9
9
|
- On an unassigned issue where the answer matters, assign it to your owner, or name in the question who should answer. Assign with `PATCH /api/v1/issues/{key}` and `{"assignee": "<email>"}`. An email nobody signed in with gets `ASSIGNEE_NOT_ALLOWED`.
|
|
10
10
|
- Never reassign an issue a human holds to get a faster answer. That is the human's call.
|
|
11
11
|
|
|
@@ -27,12 +27,12 @@ dispatch claim --issue LEGION-234 --release # I have stopped; it is free
|
|
|
27
27
|
|
|
28
28
|
## Issue status is yours to move
|
|
29
29
|
|
|
30
|
-
- Move it as a person moves a card, root and owned children: `in_progress`, `testing`, `needs_review`, then `done` once driven in production. A merge is not `done`. An issue left in `triage` while work runs is a defect. Waiting for the deploy lane is not a status and is never announced.
|
|
30
|
+
- Move it as a person moves a card, root and owned children: `in_progress`, `testing`, `needs_review`, then `done` once driven in production. A merge is not `done`. An issue left in `triage` while work runs is a defect. Waiting for the deploy lane is not a status and is never announced. Never the status of an issue with the `legion` label or under one: the daemon writes those, and a status you write there is undone.
|
|
31
31
|
- `--status done` needs `--reason`. Dispatch posts it as a message, then closes. If the close fails after the post, read the status first. A retry points its reason at the posted message.
|
|
32
32
|
- Clears differ: `--clear-priority`, `--parent ''` / `--route ''` / `--repo ''`, `--clear-external-links` / `--clear-blocked-by`. See `dispatch issue-update --help`.
|
|
33
33
|
- Repository: own `--repo`, else nearest ancestor's, else the project default, else none. Unmapped is `REPO_NOT_MAPPED`. `dispatch read` shows `Repository:`. `Repository: unknown (…)` is an older server: a `--repo` write there fails.
|
|
34
34
|
- Link the delivering PR with `--external-link` when you open it.
|
|
35
|
-
- `… is still in triage …` on a write: move the status once work starts.
|
|
35
|
+
- `… is still in triage …` on a write: move the status once work starts. It never appears on an issue with the `legion` label or under one, since the daemon moves those out of triage itself.
|
|
36
36
|
|
|
37
37
|
## Priority is yours to set
|
|
38
38
|
|