@sjawhar/pi-legion-envoy 5.23.0 → 5.23.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/envoy.js CHANGED
@@ -29654,6 +29654,8 @@ function serviceSubjectLabel(subject) {
29654
29654
  }
29655
29655
  return subject;
29656
29656
  }
29657
+ var MAX_ISSUE_PAGE_LIMIT = 250;
29658
+ var DEFAULT_ISSUE_PAGE_LIMIT = 50;
29657
29659
  var ASK_TURNS = ["human", "agent"];
29658
29660
  var DELIVERY_CAPABILITIES = ["aside", "btw", "steer"];
29659
29661
  var DELIVERY_DUPLICATE_WINDOW_MS = 72 * 60 * 60 * 1000;
@@ -30398,7 +30400,7 @@ var dispatchToolSpecs = [
30398
30400
  {
30399
30401
  name: "dispatch_issues",
30400
30402
  example: { project: "AGENTC", limit: 250, offset: 250 },
30401
- description: "List a project's issues for a roadmap or backlog pass: every issue in one project, each carrying " + "its status, priority, parent, labels, open-ask count, and route with whether it reaches anyone, " + "so you can see backlog shape without opening every issue. Optionally filter by status, parent, " + "label, priority, route status, or how recently it changed; priority takes one or more of 0-3 " + "(P0-P3) and null for an issue with no priority, so an owner's P0/P1 audit is priority [0, 1]. " + 'route_status "no_holder" lists every open issue whose route names a role nobody holds or a ' + "session that is not running at the moment of the read, whatever its priority. A restarting " + "session is absent for minutes, so an issue is unowned only when a read ten minutes later agrees. " + "Do not use it to search by keyword or phrase; dispatch_search remains the keyword surface. " + "Rows are paged after the server returns the full response: limit sets the page size (default 50, " + "max 250) and offset selects where it starts (default 0), so repeat with the next offset to " + "enumerate every matching issue.",
30403
+ description: "List a project's issues for a roadmap or backlog pass: every issue in one project, each carrying " + "its status, priority, parent, labels, open-ask count, and route with whether it reaches anyone, " + "so you can see backlog shape without opening every issue. Optionally filter by status, parent, " + "label, priority, route status, or how recently it changed; priority takes one or more of 0-3 " + "(P0-P3) and null for an issue with no priority, so an owner's P0/P1 audit is priority [0, 1]. " + 'route_status "no_holder" lists every open issue whose route names a role nobody holds or a ' + "session that is not running at the moment of the read, whatever its priority. A restarting " + "session is absent for minutes, so an issue is unowned only when a read ten minutes later agrees. " + "Do not use it to search by keyword or phrase; dispatch_search remains the keyword surface. " + "Dispatch pages the list: limit sets the page size (default " + `${DEFAULT_ISSUE_PAGE_LIMIT}, max ${MAX_ISSUE_PAGE_LIMIT}) and offset selects where it starts ` + "(default 0), and the answer names how many issues match, so repeat with the next offset to " + "walk every matching issue. A walk is exact only while the list does not change: an issue " + "that enters or leaves what the filters match, or whose status or rank changes, between two " + "pages shifts rows across a page boundary, so one issue can come back twice and another never.",
30402
30404
  arguments: (z2) => ({
30403
30405
  project: z2.string().describe("Project key to list issues from."),
30404
30406
  status: z2.enum(ISSUE_STATUSES).describe("Optional lifecycle status filter.").optional(),
@@ -30407,7 +30409,7 @@ var dispatchToolSpecs = [
30407
30409
  priority: z2.array(z2.number({ int: true, min: 0, max: 3 }).nullable(), { min: 1, max: 5 }).describe("Optional priority filter: one or more of 0 (P0, highest) through 3 (P3, lowest), and null " + "for an issue with no priority; an issue matching any listed value is returned.").optional(),
30408
30410
  updated_since: z2.string().describe("Optional RFC3339 timestamp; only issues updated at or after it.").optional(),
30409
30411
  route_status: z2.enum(ISSUE_ROUTE_STATUSES).describe("Optional: only open issues whose route is in this state. live: a running session holds " + "the role or is the routed session. no_holder: nobody running holds the role, or the " + "session is not running, right now. unknown: the Envoy listener did not answer.").optional(),
30410
- limit: z2.number({ int: true, min: 1, max: 250 }).describe("Maximum rows, 1-250; default 50.").optional(),
30412
+ limit: z2.number({ int: true, min: 1, max: MAX_ISSUE_PAGE_LIMIT }).describe(`Maximum rows, 1-${MAX_ISSUE_PAGE_LIMIT}; default ${DEFAULT_ISSUE_PAGE_LIMIT}.`).optional(),
30411
30413
  offset: z2.number({ int: true, min: 0 }).describe("Rows to skip before the page; nonnegative integer; default 0.").optional()
30412
30414
  })
30413
30415
  },
@@ -32351,8 +32353,26 @@ class DispatchClient {
32351
32353
  async resolveIssue(issueReference) {
32352
32354
  return this.#resolveIssue(issueReference);
32353
32355
  }
32354
- async listIssues(options = {}) {
32355
- return this.#json("GET", ["api", "v1", "issues"], undefined, options);
32356
+ async listIssuePage(options, page) {
32357
+ const answer = await this.#json("GET", ["api", "v1", "issues"], undefined, {
32358
+ ...options,
32359
+ limit: page.limit,
32360
+ offset: page.offset
32361
+ });
32362
+ if (Array.isArray(answer)) {
32363
+ const issues = answer;
32364
+ return {
32365
+ issues: issues.slice(page.offset, page.offset + page.limit),
32366
+ total: issues.length,
32367
+ limit: page.limit,
32368
+ offset: page.offset
32369
+ };
32370
+ }
32371
+ const served = answer;
32372
+ if (typeof served !== "object" || served === null || !Array.isArray(served.issues) || typeof served.total !== "number" || typeof served.limit !== "number" || typeof served.offset !== "number") {
32373
+ throw new Error("GET /api/v1/issues answered neither a page ({issues, total, limit, offset}) nor an array of issues");
32374
+ }
32375
+ return served;
32356
32376
  }
32357
32377
  async listProjectArtifacts(project, unlinked = false) {
32358
32378
  return this.#json("GET", ["api", "v1", "projects", project, "artifacts"], undefined, unlinked ? { unlinked: "true" } : undefined);
@@ -34002,9 +34022,7 @@ async function executeDispatchTool(input) {
34002
34022
  const priority = optionalPriorityFilter(args, "priority");
34003
34023
  const updatedSince = optionalString(args, "updated_since");
34004
34024
  const routeStatus = optionalString(args, "route_status");
34005
- const limit = Math.min(Math.max(optionalNumber(args, "limit") ?? 50, 1), 250);
34006
- const offset = Math.max(optionalNumber(args, "offset") ?? 0, 0);
34007
- const issues = await client.listIssues({
34025
+ const page = await client.listIssuePage({
34008
34026
  project,
34009
34027
  ...status === undefined ? {} : { status },
34010
34028
  ...parent === undefined ? {} : { parent },
@@ -34012,9 +34030,12 @@ async function executeDispatchTool(input) {
34012
34030
  ...priority === undefined ? {} : { priority },
34013
34031
  ...updatedSince === undefined ? {} : { updated_since: updatedSince },
34014
34032
  ...routeStatus === undefined ? {} : { route_status: routeStatus }
34033
+ }, {
34034
+ limit: optionalNumber(args, "limit") ?? DEFAULT_ISSUE_PAGE_LIMIT,
34035
+ offset: optionalNumber(args, "offset") ?? 0
34015
34036
  });
34016
- const total = issues.length;
34017
- const rows = issues.slice(offset, offset + limit).map((row) => ({
34037
+ const { total, limit, offset } = page;
34038
+ const rows = page.issues.map((row) => ({
34018
34039
  key: row.key,
34019
34040
  title: row.title,
34020
34041
  status: row.status,
package/dist/legion.js CHANGED
@@ -29823,6 +29823,8 @@ function serviceSubjectLabel(subject) {
29823
29823
  }
29824
29824
  return subject;
29825
29825
  }
29826
+ var MAX_ISSUE_PAGE_LIMIT = 250;
29827
+ var DEFAULT_ISSUE_PAGE_LIMIT = 50;
29826
29828
  var ASK_TURNS = ["human", "agent"];
29827
29829
  var DELIVERY_CAPABILITIES = ["aside", "btw", "steer"];
29828
29830
  var DELIVERY_DUPLICATE_WINDOW_MS = 72 * 60 * 60 * 1000;
@@ -30567,7 +30569,7 @@ var dispatchToolSpecs = [
30567
30569
  {
30568
30570
  name: "dispatch_issues",
30569
30571
  example: { project: "AGENTC", limit: 250, offset: 250 },
30570
- description: "List a project's issues for a roadmap or backlog pass: every issue in one project, each carrying " + "its status, priority, parent, labels, open-ask count, and route with whether it reaches anyone, " + "so you can see backlog shape without opening every issue. Optionally filter by status, parent, " + "label, priority, route status, or how recently it changed; priority takes one or more of 0-3 " + "(P0-P3) and null for an issue with no priority, so an owner's P0/P1 audit is priority [0, 1]. " + 'route_status "no_holder" lists every open issue whose route names a role nobody holds or a ' + "session that is not running at the moment of the read, whatever its priority. A restarting " + "session is absent for minutes, so an issue is unowned only when a read ten minutes later agrees. " + "Do not use it to search by keyword or phrase; dispatch_search remains the keyword surface. " + "Rows are paged after the server returns the full response: limit sets the page size (default 50, " + "max 250) and offset selects where it starts (default 0), so repeat with the next offset to " + "enumerate every matching issue.",
30572
+ description: "List a project's issues for a roadmap or backlog pass: every issue in one project, each carrying " + "its status, priority, parent, labels, open-ask count, and route with whether it reaches anyone, " + "so you can see backlog shape without opening every issue. Optionally filter by status, parent, " + "label, priority, route status, or how recently it changed; priority takes one or more of 0-3 " + "(P0-P3) and null for an issue with no priority, so an owner's P0/P1 audit is priority [0, 1]. " + 'route_status "no_holder" lists every open issue whose route names a role nobody holds or a ' + "session that is not running at the moment of the read, whatever its priority. A restarting " + "session is absent for minutes, so an issue is unowned only when a read ten minutes later agrees. " + "Do not use it to search by keyword or phrase; dispatch_search remains the keyword surface. " + "Dispatch pages the list: limit sets the page size (default " + `${DEFAULT_ISSUE_PAGE_LIMIT}, max ${MAX_ISSUE_PAGE_LIMIT}) and offset selects where it starts ` + "(default 0), and the answer names how many issues match, so repeat with the next offset to " + "walk every matching issue. A walk is exact only while the list does not change: an issue " + "that enters or leaves what the filters match, or whose status or rank changes, between two " + "pages shifts rows across a page boundary, so one issue can come back twice and another never.",
30571
30573
  arguments: (z2) => ({
30572
30574
  project: z2.string().describe("Project key to list issues from."),
30573
30575
  status: z2.enum(ISSUE_STATUSES).describe("Optional lifecycle status filter.").optional(),
@@ -30576,7 +30578,7 @@ var dispatchToolSpecs = [
30576
30578
  priority: z2.array(z2.number({ int: true, min: 0, max: 3 }).nullable(), { min: 1, max: 5 }).describe("Optional priority filter: one or more of 0 (P0, highest) through 3 (P3, lowest), and null " + "for an issue with no priority; an issue matching any listed value is returned.").optional(),
30577
30579
  updated_since: z2.string().describe("Optional RFC3339 timestamp; only issues updated at or after it.").optional(),
30578
30580
  route_status: z2.enum(ISSUE_ROUTE_STATUSES).describe("Optional: only open issues whose route is in this state. live: a running session holds " + "the role or is the routed session. no_holder: nobody running holds the role, or the " + "session is not running, right now. unknown: the Envoy listener did not answer.").optional(),
30579
- limit: z2.number({ int: true, min: 1, max: 250 }).describe("Maximum rows, 1-250; default 50.").optional(),
30581
+ limit: z2.number({ int: true, min: 1, max: MAX_ISSUE_PAGE_LIMIT }).describe(`Maximum rows, 1-${MAX_ISSUE_PAGE_LIMIT}; default ${DEFAULT_ISSUE_PAGE_LIMIT}.`).optional(),
30580
30582
  offset: z2.number({ int: true, min: 0 }).describe("Rows to skip before the page; nonnegative integer; default 0.").optional()
30581
30583
  })
30582
30584
  },
@@ -31267,8 +31269,26 @@ class DispatchClient {
31267
31269
  async resolveIssue(issueReference) {
31268
31270
  return this.#resolveIssue(issueReference);
31269
31271
  }
31270
- async listIssues(options = {}) {
31271
- return this.#json("GET", ["api", "v1", "issues"], undefined, options);
31272
+ async listIssuePage(options, page) {
31273
+ const answer = await this.#json("GET", ["api", "v1", "issues"], undefined, {
31274
+ ...options,
31275
+ limit: page.limit,
31276
+ offset: page.offset
31277
+ });
31278
+ if (Array.isArray(answer)) {
31279
+ const issues = answer;
31280
+ return {
31281
+ issues: issues.slice(page.offset, page.offset + page.limit),
31282
+ total: issues.length,
31283
+ limit: page.limit,
31284
+ offset: page.offset
31285
+ };
31286
+ }
31287
+ const served = answer;
31288
+ if (typeof served !== "object" || served === null || !Array.isArray(served.issues) || typeof served.total !== "number" || typeof served.limit !== "number" || typeof served.offset !== "number") {
31289
+ throw new Error("GET /api/v1/issues answered neither a page ({issues, total, limit, offset}) nor an array of issues");
31290
+ }
31291
+ return served;
31272
31292
  }
31273
31293
  async listProjectArtifacts(project, unlinked = false) {
31274
31294
  return this.#json("GET", ["api", "v1", "projects", project, "artifacts"], undefined, unlinked ? { unlinked: "true" } : undefined);
@@ -32941,9 +32961,7 @@ async function executeDispatchTool(input) {
32941
32961
  const priority = optionalPriorityFilter(args, "priority");
32942
32962
  const updatedSince = optionalString(args, "updated_since");
32943
32963
  const routeStatus = optionalString(args, "route_status");
32944
- const limit = Math.min(Math.max(optionalNumber(args, "limit") ?? 50, 1), 250);
32945
- const offset = Math.max(optionalNumber(args, "offset") ?? 0, 0);
32946
- const issues = await client.listIssues({
32964
+ const page = await client.listIssuePage({
32947
32965
  project,
32948
32966
  ...status === undefined ? {} : { status },
32949
32967
  ...parent === undefined ? {} : { parent },
@@ -32951,9 +32969,12 @@ async function executeDispatchTool(input) {
32951
32969
  ...priority === undefined ? {} : { priority },
32952
32970
  ...updatedSince === undefined ? {} : { updated_since: updatedSince },
32953
32971
  ...routeStatus === undefined ? {} : { route_status: routeStatus }
32972
+ }, {
32973
+ limit: optionalNumber(args, "limit") ?? DEFAULT_ISSUE_PAGE_LIMIT,
32974
+ offset: optionalNumber(args, "offset") ?? 0
32954
32975
  });
32955
- const total = issues.length;
32956
- const rows = issues.slice(offset, offset + limit).map((row) => ({
32976
+ const { total, limit, offset } = page;
32977
+ const rows = page.issues.map((row) => ({
32957
32978
  key: row.key,
32958
32979
  title: row.title,
32959
32980
  status: row.status,
@@ -33502,7 +33523,7 @@ import { logger } from "@oh-my-pi/pi-utils";
33502
33523
  // package.json
33503
33524
  var package_default = {
33504
33525
  name: "@sjawhar/pi-legion-envoy",
33505
- version: "5.23.0",
33526
+ version: "5.23.1",
33506
33527
  type: "module",
33507
33528
  omp: {
33508
33529
  extensions: [
@@ -26,3 +26,14 @@ A path Dispatch does not serve under `/api` or `/v1` answers
26
26
  route"}`; when you see that, you typed the path wrong — read the index rather than guessing. Every
27
27
  `/api/v1` error body carries a `code`; branch on the code, never on the text.
28
28
 
29
+ `GET /api/v1/issues` answers every matching issue as an array, or one page when you name `limit`
30
+ (1–250) or `offset` (alone it pages 50): `{issues, total, limit, offset}`, where `total` counts
31
+ every issue your filters match. Walking the pages with the next `offset` is exact only while the
32
+ listing does not change: an issue that enters or leaves what the filters match, or whose status or
33
+ rank changes, between two reads shifts rows across a page boundary, and one issue comes back twice
34
+ and another never, even when you stop at `total`. The unpaged array is the only exact set one read
35
+ gives. `cursor` is `400 INVALID_QUERY`. `dispatch_issues` pages for you.
36
+ The event logs page with `after` or `before` and `limit`, and `GET /api/v1/search` takes a `limit`
37
+ of at most 50 and has no next page. Every other route answers without paging and ignores a paging
38
+ parameter.
39
+
@@ -144,8 +144,11 @@ when it last changed — a roadmap or backlog pass without opening every issue.
144
144
  (a lifecycle status), `parent` (one issue's children), `label`, `priority` (a list of `0`–`3`, with
145
145
  `null` for an issue with no priority: `[0, 1]` is every P0 and P1), `route_status` (below), or
146
146
  `updated_since` (an RFC3339 timestamp, for "what moved this week"). `limit` is the page size, 50 by
147
- default and 250 at most, and `offset` is where the page starts: repeat with the next offset until a
148
- page comes back short to list every matching issue.
147
+ default and 250 at most, and `offset` is where the page starts; the answer says how many issues
148
+ match. Repeating with the next offset walks every matching issue only while the list does not
149
+ change: an issue that enters or leaves what your filters match, or whose status or rank changes,
150
+ between two pages shifts rows across a page boundary, and the walk then shows one issue twice and
151
+ misses another.
149
152
 
150
153
  This is not search: it matches no text. Use `dispatch_search` for a keyword or phrase, and
151
154
  `dispatch_issues` when you want every issue in a project and its current state.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion-envoy",
3
- "version": "5.23.0",
3
+ "version": "5.23.1",
4
4
  "type": "module",
5
5
  "omp": {
6
6
  "extensions": [