@stigmer/cli 3.12.3 → 3.12.4
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/commands/list.js +7 -15
- package/commands/list.js.map +1 -1
- package/package.json +5 -5
- package/registry/verb-support.d.ts.map +1 -1
- package/registry/verb-support.js +6 -3
- package/registry/verb-support.js.map +1 -1
- package/resources/list.d.ts.map +1 -1
- package/resources/list.js +39 -9
- package/resources/list.js.map +1 -1
- package/resources/session.d.ts +0 -8
- package/resources/session.d.ts.map +1 -1
- package/resources/session.js +6 -33
- package/resources/session.js.map +1 -1
- package/resources/skill.d.ts +0 -10
- package/resources/skill.d.ts.map +1 -1
- package/resources/skill.js +8 -59
- package/resources/skill.js.map +1 -1
- package/resources/stream/events.d.ts +1 -1
- package/resources/stream/events.d.ts.map +1 -1
- package/resources/stream/events.js +8 -6
- package/resources/stream/events.js.map +1 -1
- package/resources/stream/render-plaintext.d.ts.map +1 -1
- package/resources/stream/render-plaintext.js +19 -5
- package/resources/stream/render-plaintext.js.map +1 -1
- package/src/commands/list.ts +7 -17
- package/src/registry/registry.test.ts +50 -8
- package/src/registry/verb-support.ts +6 -3
- package/src/resources/list.ts +40 -9
- package/src/resources/read-completion.integration.test.ts +14 -4
- package/src/resources/session.ts +6 -39
- package/src/resources/skill.test.ts +5 -77
- package/src/resources/skill.ts +8 -67
- package/src/resources/stream/events.ts +9 -7
- package/src/resources/stream/render-plaintext.test.ts +51 -0
- package/src/resources/stream/render-plaintext.ts +24 -6
- package/src/resources/session.test.ts +0 -48
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/resources/stream/events.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/resources/stream/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
// The sealed event model for the headless snapshot differ.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
3
|
+
// Inherited from the retired Go CLI's pkg/executiontui.Event family (deleted
|
|
4
|
+
// with the TypeScript migration, PR #203). The differ projects each
|
|
5
|
+
// AgentExecution snapshot into a sequence of these discrete events; the
|
|
5
6
|
// NDJSON and plaintext renderers consume them. The Ink TTY path does NOT use
|
|
6
|
-
// this model — it renders from full snapshots via @stigmer/
|
|
7
|
+
// this model — it renders from full snapshots via @stigmer/ink.
|
|
7
8
|
//
|
|
8
|
-
// `kind` is the discriminant. The NDJSON renderer
|
|
9
|
-
// wire taxonomy (run_stream_json.go)
|
|
10
|
-
//
|
|
9
|
+
// `kind` is the discriminant. The NDJSON renderer preserves the retired Go
|
|
10
|
+
// CLI's exact wire taxonomy (its run_stream_json.go) — that output is a
|
|
11
|
+
// machine contract consumers may parse — so kinds are deliberately 1:1 with
|
|
12
|
+
// the original event types rather than the wire strings.
|
|
11
13
|
/** True for the two events that end a stream. */
|
|
12
14
|
export function isTerminalEvent(event) {
|
|
13
15
|
return event.kind === "done" || event.kind === "streamError";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../../src/resources/stream/events.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../../src/resources/stream/events.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,EAAE;AACF,6EAA6E;AAC7E,oEAAoE;AACpE,wEAAwE;AACxE,6EAA6E;AAC7E,gEAAgE;AAChE,EAAE;AACF,2EAA2E;AAC3E,wEAAwE;AACxE,4EAA4E;AAC5E,yDAAyD;AAkMzD,iDAAiD;AACjD,MAAM,UAAU,eAAe,CAAC,KAAkB;IAChD,OAAO,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,CAAC;AAC/D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-plaintext.d.ts","sourceRoot":"","sources":["../../../src/resources/stream/render-plaintext.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,8DAA8D,CAAC;
|
|
1
|
+
{"version":3,"file":"render-plaintext.d.ts","sourceRoot":"","sources":["../../../src/resources/stream/render-plaintext.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,8DAA8D,CAAC;AAE9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAgB,MAAM,aAAa,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,MAAM,WAAW,wBAAwB;IACvC,+BAA+B;IAC/B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC7B;AAID,wEAAwE;AACxE,qBAAa,iBAAkB,YAAW,gBAAgB;IAC5C,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,wBAAwB;IAE3D,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAqDhC,eAAe,CAAC,MAAM,EAAE,mBAAmB,GAAG,cAAc;CAG7D"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// Plaintext headless renderer for non-TTY output (piped stdout).
|
|
2
2
|
//
|
|
3
|
-
//
|
|
3
|
+
// Inherited from the retired Go CLI's streamAgentPlainText: clean AI text to
|
|
4
4
|
// the data sink (stdout), terse tool/status lines to the status sink (stderr).
|
|
5
5
|
// No ANSI, no frames. Approvals are auto-skipped — there is no keyboard here;
|
|
6
6
|
// use --json for a machine-readable non-TTY stream. Phase/done/stream-error are
|
|
7
7
|
// not rendered: the driver returns the outcome and the command surfaces it.
|
|
8
8
|
import { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
9
|
+
import { resolveToolKindByName, shellIntentFromArgs } from "@stigmer/sdk";
|
|
9
10
|
const MAX_ERROR_LEN = 100;
|
|
10
11
|
/** Renders a terse, ANSI-free transcript; auto-skips every approval. */
|
|
11
12
|
export class PlaintextRenderer {
|
|
@@ -30,15 +31,15 @@ export class PlaintextRenderer {
|
|
|
30
31
|
status.write(`\n> ${event.content}\n\n`);
|
|
31
32
|
return;
|
|
32
33
|
case "toolRunning":
|
|
33
|
-
status.write(` ⠋ ${event.toolCall
|
|
34
|
+
status.write(` ⠋ ${toolLine(event.toolCall)}\n`);
|
|
34
35
|
return;
|
|
35
36
|
case "toolCompleted":
|
|
36
37
|
status.write(event.toolCall.error !== ""
|
|
37
|
-
? ` ✗ ${event.toolCall
|
|
38
|
-
: ` ✓ ${event.toolCall
|
|
38
|
+
? ` ✗ ${toolLine(event.toolCall)}: ${truncate(event.toolCall.error, MAX_ERROR_LEN)}\n`
|
|
39
|
+
: ` ✓ ${toolLine(event.toolCall)}\n`);
|
|
39
40
|
return;
|
|
40
41
|
case "toolInterrupted":
|
|
41
|
-
status.write(` ⊘ ${event.toolCall
|
|
42
|
+
status.write(` ⊘ ${toolLine(event.toolCall)} (interrupted)\n`);
|
|
42
43
|
return;
|
|
43
44
|
case "approvalNeeded":
|
|
44
45
|
status.write(` ⚠ Approval needed: ${event.toolName} (auto-skipped in non-TTY mode)\n`);
|
|
@@ -66,6 +67,19 @@ export class PlaintextRenderer {
|
|
|
66
67
|
return ApprovalAction.SKIP;
|
|
67
68
|
}
|
|
68
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* The status-line label for a tool call: the model-authored intent phrase
|
|
72
|
+
* when the shell call carries one (stigmer#276) — the same convention that
|
|
73
|
+
* titles rows in the web console and the Ink TTY path — otherwise the raw
|
|
74
|
+
* tool name. Kind is resolved by name only: the snapshot projection carries
|
|
75
|
+
* neither `tool_kind` nor `mcp_server_slug`, and MCP tools never carry an
|
|
76
|
+
* intent (the fixture pins them to null), so name-lookup is sufficient here.
|
|
77
|
+
* NDJSON is deliberately untouched — it is the machine contract.
|
|
78
|
+
*/
|
|
79
|
+
function toolLine(toolCall) {
|
|
80
|
+
const intent = shellIntentFromArgs(resolveToolKindByName(toolCall.name), toolCall.args);
|
|
81
|
+
return intent ?? toolCall.name;
|
|
82
|
+
}
|
|
69
83
|
// Trim and ellipsize. Mirrors Go's truncate.
|
|
70
84
|
function truncate(value, maxLen) {
|
|
71
85
|
const trimmed = value.trim();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-plaintext.js","sourceRoot":"","sources":["../../../src/resources/stream/render-plaintext.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,EAAE;AACF,
|
|
1
|
+
{"version":3,"file":"render-plaintext.js","sourceRoot":"","sources":["../../../src/resources/stream/render-plaintext.ts"],"names":[],"mappings":"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"}
|
package/src/commands/list.ts
CHANGED
|
@@ -35,16 +35,18 @@ async function runList(type: string, options: ListFlags, command: Command): Prom
|
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// The one pre-gate alias family: executions resolve BEFORE the registry.
|
|
39
|
+
// Adding another pre-gate alias here requires its kind to be
|
|
40
|
+
// non-registry-addressable (agent_execution's posture) — an addressable
|
|
41
|
+
// kind must list through the registry dispatch instead, or its verb-matrix
|
|
42
|
+
// row lies. The alias-shadowing pin in registry/registry.test.ts enforces
|
|
43
|
+
// this; sessions shipped works-but-unadvertised through exactly such a
|
|
44
|
+
// bypass for two months (stigmer/stigmer#469).
|
|
38
45
|
const { isExecutionAlias } = await import("../resources/execution.js");
|
|
39
|
-
const { isSessionAlias } = await import("../resources/session.js");
|
|
40
46
|
if (isExecutionAlias(type)) {
|
|
41
47
|
await runListExecutions(options, command);
|
|
42
48
|
return;
|
|
43
49
|
}
|
|
44
|
-
if (isSessionAlias(type)) {
|
|
45
|
-
await runListSessions(options);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
50
|
|
|
49
51
|
const info = defaultRegistry().getByAlias(type);
|
|
50
52
|
if (info === undefined) {
|
|
@@ -95,18 +97,6 @@ async function runListExecutions(options: ListFlags, command: Command): Promise<
|
|
|
95
97
|
process.stdout.write(execution.renderExecutionList(result, format, "agent"));
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
async function runListSessions(options: ListFlags): Promise<void> {
|
|
99
|
-
const [{ connectBackend }, session] = await Promise.all([
|
|
100
|
-
import("../backend.js"),
|
|
101
|
-
import("../resources/session.js"),
|
|
102
|
-
]);
|
|
103
|
-
|
|
104
|
-
const client = connectBackend();
|
|
105
|
-
ensureAuthenticated(client.config);
|
|
106
|
-
const result = await session.listSessions(client.stigmer, parseLimit(options.limit));
|
|
107
|
-
process.stdout.write(session.renderSessionList(result, readFormat(options)));
|
|
108
|
-
}
|
|
109
|
-
|
|
110
100
|
function isTypesAlias(type: string): boolean {
|
|
111
101
|
const normalized = type.trim().toLowerCase();
|
|
112
102
|
return normalized === "type" || normalized === "types";
|
|
@@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest";
|
|
|
3
3
|
import { APPLY_HANDLERS } from "../resources/apply/handlers.js";
|
|
4
4
|
import { DELETE_HANDLERS } from "../resources/delete.js";
|
|
5
5
|
import { GET_BINDINGS } from "../resources/get-bindings.js";
|
|
6
|
+
import { isExecutionAlias } from "../resources/execution.js";
|
|
6
7
|
import { LIST_HANDLERS, SEARCH_KINDS } from "../resources/list.js";
|
|
7
8
|
import { VALIDATE_SCHEMAS } from "../resources/validate.js";
|
|
8
9
|
import { normalizeAlias } from "./aliases.js";
|
|
@@ -55,8 +56,14 @@ describe("registry — alias resolution", () => {
|
|
|
55
56
|
expect(registry.getByAlias("nope")).toBeUndefined();
|
|
56
57
|
});
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
// The runtime execution kinds are served by the pre-gate `list executions`
|
|
60
|
+
// route and their dedicated controllers; registering either would put a
|
|
61
|
+
// row in `list types` that the pre-gate route then shadows — the
|
|
62
|
+
// stigmer/stigmer#469 class (see the alias-shadowing suite below).
|
|
63
|
+
it("does not register the runtime execution kinds as addressable types", () => {
|
|
64
|
+
for (const kind of [ApiResourceKind.agent_execution, ApiResourceKind.workflow_execution]) {
|
|
65
|
+
expect(registry.getByKind(kind)).toBeUndefined();
|
|
66
|
+
}
|
|
60
67
|
});
|
|
61
68
|
});
|
|
62
69
|
|
|
@@ -102,12 +109,18 @@ describe("registry — verb support matrix", () => {
|
|
|
102
109
|
expect(registry.supportsVerb(ApiResourceKind.schedule, Verb.Run)).toBe(false);
|
|
103
110
|
});
|
|
104
111
|
|
|
105
|
-
it("
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
it("agent_share promises only apply (narrowed — stigmer/stigmer#354)", () => {
|
|
113
|
+
expect(registry.supportsVerb(ApiResourceKind.agent_share, Verb.Apply)).toBe(true);
|
|
114
|
+
for (const v of [Verb.Get, Verb.List, Verb.Delete]) {
|
|
115
|
+
expect(registry.supportsVerb(ApiResourceKind.agent_share, v)).toBe(false);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("session promises apply + list; get/delete stay narrowed (stigmer/stigmer#354, #469)", () => {
|
|
120
|
+
expect(registry.supportsVerb(ApiResourceKind.session, Verb.Apply)).toBe(true);
|
|
121
|
+
expect(registry.supportsVerb(ApiResourceKind.session, Verb.List)).toBe(true);
|
|
122
|
+
for (const v of [Verb.Get, Verb.Delete]) {
|
|
123
|
+
expect(registry.supportsVerb(ApiResourceKind.session, v)).toBe(false);
|
|
111
124
|
}
|
|
112
125
|
});
|
|
113
126
|
|
|
@@ -268,3 +281,32 @@ describe("registry — verb/dispatch conformance", () => {
|
|
|
268
281
|
}
|
|
269
282
|
});
|
|
270
283
|
});
|
|
284
|
+
|
|
285
|
+
// `stigmer list` resolves a handful of aliases BEFORE consulting the
|
|
286
|
+
// registry (commands/list.ts): `types` (a command word, not a kind) and the
|
|
287
|
+
// execution family. A pre-gate alias that intercepts a REGISTERED kind hides
|
|
288
|
+
// that kind's registry dispatch behind bespoke behavior the verb matrix
|
|
289
|
+
// cannot describe — sessions shipped working-but-unadvertised for two months
|
|
290
|
+
// exactly this way (stigmer/stigmer#469). Every pre-gate predicate must be
|
|
291
|
+
// listed here; the pin holds each one away from every registered kind's
|
|
292
|
+
// alias set, so a future bypass for an addressable kind is a red test, not
|
|
293
|
+
// a silently lying `list types` row.
|
|
294
|
+
describe("registry — pre-gate list aliases cannot shadow registered kinds", () => {
|
|
295
|
+
const PRE_GATE_PREDICATES: ReadonlyArray<{ name: string; matches: (type: string) => boolean }> = [
|
|
296
|
+
{ name: "executions (resources/execution.ts isExecutionAlias)", matches: isExecutionAlias },
|
|
297
|
+
];
|
|
298
|
+
|
|
299
|
+
it("no registered kind's alias is intercepted by a pre-gate predicate", () => {
|
|
300
|
+
for (const { name, matches } of PRE_GATE_PREDICATES) {
|
|
301
|
+
for (const info of registry.all()) {
|
|
302
|
+
for (const alias of info.aliases) {
|
|
303
|
+
expect(
|
|
304
|
+
matches(alias),
|
|
305
|
+
`registry alias '${alias}' (${info.name}) is intercepted by the pre-gate route ${name} — ` +
|
|
306
|
+
"it shadows the kind's registry dispatch (the stigmer/stigmer#469 works-but-unadvertised class)",
|
|
307
|
+
).toBe(false);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
});
|
|
@@ -57,9 +57,12 @@ export const VERB_SUPPORT: ReadonlyMap<ApiResourceKind, ReadonlySet<Verb>> = new
|
|
|
57
57
|
[ApiResourceKind.agent_instance, new Set<Verb>([Verb.Apply, Verb.Get, Verb.List, Verb.Delete])],
|
|
58
58
|
// No list: proto exposes getByWorkflow (requires workflow_id), not a generic list.
|
|
59
59
|
[ApiResourceKind.workflow_instance, new Set<Verb>([Verb.Apply, Verb.Get, Verb.Delete])],
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
|
|
60
|
+
// Session list is served by its dedicated query RPC (a LIST_HANDLERS
|
|
61
|
+
// entry); stigmer/stigmer#469 promoted it into the matrix after it shipped
|
|
62
|
+
// working-but-unadvertised through a pre-gate bypass. get/delete remain
|
|
63
|
+
// deliberately unpromised until a real read/ops story exists
|
|
64
|
+
// (stigmer/stigmer#354) — resume-style flows go through `stigmer session`.
|
|
65
|
+
[ApiResourceKind.session, new Set<Verb>([Verb.Apply, Verb.List])],
|
|
63
66
|
]);
|
|
64
67
|
|
|
65
68
|
export function verbsForKind(kind: ApiResourceKind): ReadonlySet<Verb> {
|
package/src/resources/list.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// `list` dispatch: render a collection of resources for a kind.
|
|
2
2
|
//
|
|
3
|
-
// Most registry kinds
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
3
|
+
// Most registry kinds list through the unified SearchService. The rest take
|
|
4
|
+
// dedicated find/list RPCs with bespoke table shapes, registered in
|
|
5
|
+
// LIST_HANDLERS — either because the kind is not search-indexed (API keys,
|
|
6
|
+
// agent channels, channel apps, schedules) or because the dedicated RPC is
|
|
7
|
+
// the better list surface than the kind's search index (organizations are
|
|
8
|
+
// caller-scoped; agent instances and sessions carry columns search results
|
|
9
|
+
// don't have). Same map-dispatch shape as the get, delete, and apply
|
|
10
|
+
// registries, so the conformance test in registry/registry.test.ts can hold
|
|
11
|
+
// all four to the verb matrix.
|
|
10
12
|
//
|
|
11
13
|
// Handlers FETCH, the dispatcher RENDERS: every handler returns entries plus
|
|
12
14
|
// its schema/table pair, and listResources alone applies --limit and renders.
|
|
@@ -23,6 +25,8 @@ import { ChannelAppSchema } from "@stigmer/protos/ai/stigmer/agentic/channelapp/
|
|
|
23
25
|
import { ListChannelAppsByOrgInputSchema } from "@stigmer/protos/ai/stigmer/agentic/channelapp/v1/io_pb";
|
|
24
26
|
import { ScheduleSchema } from "@stigmer/protos/ai/stigmer/agentic/schedule/v1/api_pb";
|
|
25
27
|
import { ListSchedulesRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/schedule/v1/io_pb";
|
|
28
|
+
import { SessionSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/api_pb";
|
|
29
|
+
import { ListSessionsRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/io_pb";
|
|
26
30
|
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
27
31
|
import { PageInfoSchema } from "@stigmer/protos/ai/stigmer/commons/rpc/pagination_pb";
|
|
28
32
|
import { ApiKeySchema } from "@stigmer/protos/ai/stigmer/iam/apikey/v1/api_pb";
|
|
@@ -37,8 +41,8 @@ import { bool, type JsonObject, obj, renderCollection, str, type TableShape } fr
|
|
|
37
41
|
// scope). Must stay in step with the server's SearchableKinds allowlist
|
|
38
42
|
// (backend/services/stigmer-server/pkg/query/search/valueobject/
|
|
39
43
|
// search_criteria.go): a kind present here but absent there silently lists
|
|
40
|
-
// as empty. The LIST_HANDLERS kinds are deliberately NOT here —
|
|
41
|
-
//
|
|
44
|
+
// as empty. The LIST_HANDLERS kinds are deliberately NOT here — see the
|
|
45
|
+
// header: not search-indexed, or better served by their dedicated RPC.
|
|
42
46
|
export const SEARCH_KINDS: ReadonlySet<ApiResourceKind> = new Set<ApiResourceKind>([
|
|
43
47
|
ApiResourceKind.agent,
|
|
44
48
|
ApiResourceKind.workflow,
|
|
@@ -129,6 +133,19 @@ export const LIST_HANDLERS: ReadonlyMap<ApiResourceKind, ListFn> = new Map<ApiRe
|
|
|
129
133
|
return { schema: ScheduleSchema, entries: result.items, table: SCHEDULE_TABLE };
|
|
130
134
|
},
|
|
131
135
|
],
|
|
136
|
+
[
|
|
137
|
+
ApiResourceKind.session,
|
|
138
|
+
async (client, _org, limit) => {
|
|
139
|
+
// Sessions ARE search-indexed, but list deliberately uses the dedicated
|
|
140
|
+
// RPC: the table's columns (agent instance, subject) don't exist on
|
|
141
|
+
// SearchResult rows, and the RPC is caller-scoped like organization and
|
|
142
|
+
// api_key above — the agent_instance precedent, not an oversight.
|
|
143
|
+
// Promoted from a bespoke pre-gate route in commands/list.ts by
|
|
144
|
+
// stigmer/stigmer#469 (it had shipped working-but-unadvertised).
|
|
145
|
+
const result = await client.session.list(create(ListSessionsRequestSchema, { pageSize: limit }));
|
|
146
|
+
return { schema: SessionSchema, entries: result.entries, table: SESSION_TABLE };
|
|
147
|
+
},
|
|
148
|
+
],
|
|
132
149
|
]);
|
|
133
150
|
|
|
134
151
|
export async function listResources(
|
|
@@ -177,6 +194,20 @@ export const SEARCH_TABLE: TableShape = {
|
|
|
177
194
|
],
|
|
178
195
|
};
|
|
179
196
|
|
|
197
|
+
// A session's identity for follow-up commands is its ID (`stigmer session
|
|
198
|
+
// resume <id>`), so the ID leads; agent instance and subject are how a human
|
|
199
|
+
// recognizes which conversation it was.
|
|
200
|
+
const SESSION_TABLE: TableShape = {
|
|
201
|
+
resourceName: "sessions",
|
|
202
|
+
headers: ["SESSION ID", "AGENT", "SUBJECT", "CREATED"],
|
|
203
|
+
row: (json) => [
|
|
204
|
+
str(obj(json, "metadata"), "id"),
|
|
205
|
+
str(obj(json, "spec"), "agent_instance_id") || "-",
|
|
206
|
+
truncate(str(obj(json, "spec"), "subject") || "-", 50),
|
|
207
|
+
date(str(obj(json, "metadata"), "created_at")),
|
|
208
|
+
],
|
|
209
|
+
};
|
|
210
|
+
|
|
180
211
|
const INSTANCE_TABLE: TableShape = {
|
|
181
212
|
resourceName: "agent instances",
|
|
182
213
|
headers: ["ID", "SLUG", "AGENT", "DESCRIPTION"],
|
|
@@ -25,9 +25,9 @@ import type { AddressInfo } from "node:net";
|
|
|
25
25
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
26
26
|
import { classify, ExitCode } from "../errors/index.js";
|
|
27
27
|
import { getExecution, listAgentExecutions, renderExecutionList } from "./execution.js";
|
|
28
|
+
import { listResources } from "./list.js";
|
|
28
29
|
import { renderResource } from "./render.js";
|
|
29
30
|
import { searchResources } from "./search.js";
|
|
30
|
-
import { listSessions, renderSessionList } from "./session.js";
|
|
31
31
|
import { getSessionUsageReport, renderSessionUsage } from "./usage.js";
|
|
32
32
|
|
|
33
33
|
const knownExec = create(AgentExecutionSchema, {
|
|
@@ -141,10 +141,20 @@ describe("execution integration", () => {
|
|
|
141
141
|
});
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
+
// Session list runs through the same registry dispatch as every other kind
|
|
145
|
+
// (LIST_HANDLERS; promoted from a bespoke route by stigmer/stigmer#469).
|
|
144
146
|
describe("session integration", () => {
|
|
145
|
-
it("lists sessions as a human table", async () => {
|
|
146
|
-
const
|
|
147
|
-
expect(
|
|
147
|
+
it("lists sessions through the registry dispatch as a human table", async () => {
|
|
148
|
+
const rendered = await listResources(client, ApiResourceKind.session, "", 50, "table");
|
|
149
|
+
expect(rendered).toContain("SESSION ID");
|
|
150
|
+
expect(rendered).toContain("ses_1");
|
|
151
|
+
expect(rendered).toContain("Fix the build");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("renders session list json as the entries array every list kind shares", async () => {
|
|
155
|
+
const rendered = await listResources(client, ApiResourceKind.session, "", 50, "json");
|
|
156
|
+
const json = JSON.parse(rendered);
|
|
157
|
+
expect(json).toEqual([toJson(SessionSchema, knownSession, { useProtoFieldName: true })]);
|
|
148
158
|
});
|
|
149
159
|
});
|
|
150
160
|
|
package/src/resources/session.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// Read helpers for the dedicated `stigmer session` command surface (resume
|
|
2
|
+
// and friends). Session LIST is a first-class registry verb served by
|
|
3
|
+
// LIST_HANDLERS in resources/list.ts — stigmer/stigmer#469 promoted it there
|
|
4
|
+
// from a bespoke pre-gate route in commands/list.ts. get/delete stay
|
|
5
|
+
// deliberately unpromised in the verb matrix (stigmer/stigmer#354);
|
|
6
|
+
// resume-style flows read sessions through these helpers instead.
|
|
5
7
|
|
|
6
8
|
import { create } from "@bufbuild/protobuf";
|
|
7
9
|
import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
8
10
|
import { ListAgentExecutionsBySessionRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/io_pb";
|
|
9
11
|
import type { Session } from "@stigmer/protos/ai/stigmer/agentic/session/v1/api_pb";
|
|
10
|
-
import { ListSessionsRequestSchema, SessionListSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/io_pb";
|
|
11
12
|
import type { Stigmer } from "@stigmer/sdk";
|
|
12
|
-
import type { OutputFormat } from "../output/index.js";
|
|
13
|
-
import type { ResourceResult } from "./get-bindings.js";
|
|
14
|
-
import { obj, renderListMessage, str, type TableShape } from "./render.js";
|
|
15
13
|
|
|
16
14
|
// Mirrors Go's execution.MaxPageSize — fetch the whole session history in one
|
|
17
15
|
// call (sessions don't accumulate enough executions to need pagination here).
|
|
@@ -33,34 +31,3 @@ export async function listExecutionsBySession(client: Stigmer, sessionId: string
|
|
|
33
31
|
return list.entries;
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
/** True for the `session`/`sessions` type-alias family. */
|
|
37
|
-
export function isSessionAlias(type: string): boolean {
|
|
38
|
-
const normalized = type.trim().toLowerCase();
|
|
39
|
-
return normalized === "session" || normalized === "sessions";
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** List sessions for the current context, paginated by `limit`. */
|
|
43
|
-
export async function listSessions(client: Stigmer, limit: number): Promise<ResourceResult> {
|
|
44
|
-
const message = await client.session.list(create(ListSessionsRequestSchema, { pageSize: limit }));
|
|
45
|
-
return { schema: SessionListSchema, message };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const SESSION_TABLE: TableShape = {
|
|
49
|
-
resourceName: "sessions",
|
|
50
|
-
headers: ["SESSION ID", "AGENT", "SUBJECT", "CREATED"],
|
|
51
|
-
row: (json) => [
|
|
52
|
-
str(obj(json, "metadata"), "id"),
|
|
53
|
-
dash(str(obj(json, "spec"), "agent_instance_id")),
|
|
54
|
-
dash(str(obj(json, "spec"), "subject")),
|
|
55
|
-
dash(str(obj(json, "metadata"), "created_at")),
|
|
56
|
-
],
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
/** Render a session list (json/yaml = full envelope; table = grid). */
|
|
60
|
-
export function renderSessionList(result: ResourceResult, format: OutputFormat): string {
|
|
61
|
-
return renderListMessage(result.schema, result.message, format, SESSION_TABLE);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function dash(value: string): string {
|
|
65
|
-
return value === "" ? "-" : value;
|
|
66
|
-
}
|
|
@@ -4,9 +4,6 @@
|
|
|
4
4
|
import { mkdirSync, mkdtempSync, rmSync, utimesSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { join } from "node:path";
|
|
7
|
-
import { create } from "@bufbuild/protobuf";
|
|
8
|
-
import { Code, ConnectError } from "@connectrpc/connect";
|
|
9
|
-
import { PushSkillRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/io_pb";
|
|
10
7
|
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
11
8
|
import type { Stigmer } from "@stigmer/sdk";
|
|
12
9
|
import { unzipSync, zipSync } from "fflate";
|
|
@@ -19,7 +16,6 @@ import {
|
|
|
19
16
|
hasSkillFile,
|
|
20
17
|
parseSkillMetadata,
|
|
21
18
|
parseVisibility,
|
|
22
|
-
pushRouted,
|
|
23
19
|
pushSkill,
|
|
24
20
|
pushSkillFromArchive,
|
|
25
21
|
readSkillArchive,
|
|
@@ -354,76 +350,8 @@ describe("formatBytes / shortHash", () => {
|
|
|
354
350
|
});
|
|
355
351
|
});
|
|
356
352
|
|
|
357
|
-
//
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
function routingFakeClient(overrides: Record<string, any> = {}) {
|
|
363
|
-
const calls = { push: [] as any[], mint: [] as any[] };
|
|
364
|
-
const client = {
|
|
365
|
-
skill: {
|
|
366
|
-
async push(req: any) {
|
|
367
|
-
calls.push.push(req);
|
|
368
|
-
return { metadata: { id: "skill_x" } };
|
|
369
|
-
},
|
|
370
|
-
async createArtifactUploadUrl(req: any) {
|
|
371
|
-
calls.mint.push(req);
|
|
372
|
-
return { url: "http://localhost:7234/v1/skill-artifacts/uploads/sau_t", artifactUploadRef: "sau_t", ttlSeconds: 900 };
|
|
373
|
-
},
|
|
374
|
-
...overrides,
|
|
375
|
-
},
|
|
376
|
-
} as any;
|
|
377
|
-
return { client, calls };
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
afterEach(() => {
|
|
381
|
-
vi.unstubAllGlobals();
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
it("keeps small artifacts inline (no mint, bytes in the request)", async () => {
|
|
385
|
-
const { client, calls } = routingFakeClient();
|
|
386
|
-
const req = create(PushSkillRequestSchema, { org: "acme", artifact: new Uint8Array(1024) });
|
|
387
|
-
|
|
388
|
-
await pushRouted(client, req);
|
|
389
|
-
|
|
390
|
-
expect(calls.mint).toHaveLength(0);
|
|
391
|
-
expect(calls.push).toHaveLength(1);
|
|
392
|
-
expect(calls.push[0].artifact.length).toBe(1024);
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
it("stages large artifacts over HTTP and pushes by reference", async () => {
|
|
396
|
-
const { client, calls } = routingFakeClient();
|
|
397
|
-
const artifact = new Uint8Array(INLINE_MAX + 1);
|
|
398
|
-
const req = create(PushSkillRequestSchema, { org: "acme", artifact, tag: "stable", message: "big" });
|
|
399
|
-
|
|
400
|
-
let putBytes = 0;
|
|
401
|
-
vi.stubGlobal("fetch", vi.fn(async (_url: string, init: any) => {
|
|
402
|
-
putBytes = init.body.length;
|
|
403
|
-
return { ok: true } as any;
|
|
404
|
-
}));
|
|
405
|
-
|
|
406
|
-
await pushRouted(client, req);
|
|
407
|
-
|
|
408
|
-
expect(calls.mint).toHaveLength(1);
|
|
409
|
-
expect(calls.mint[0].sizeBytes).toBe(BigInt(artifact.length));
|
|
410
|
-
expect(putBytes).toBe(artifact.length);
|
|
411
|
-
expect(calls.push).toHaveLength(1);
|
|
412
|
-
expect(calls.push[0].artifact.length).toBe(0);
|
|
413
|
-
expect(calls.push[0].artifactUploadRef).toBe("sau_t");
|
|
414
|
-
// The rewrite must not lose the rest of the request.
|
|
415
|
-
expect(calls.push[0].tag).toBe("stable");
|
|
416
|
-
expect(calls.push[0].message).toBe("big");
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
it("fails loud against servers that predate the transfer lane", async () => {
|
|
420
|
-
const { client } = routingFakeClient({
|
|
421
|
-
async createArtifactUploadUrl() {
|
|
422
|
-
throw new ConnectError("unknown method", Code.Unimplemented);
|
|
423
|
-
},
|
|
424
|
-
});
|
|
425
|
-
const req = create(PushSkillRequestSchema, { org: "acme", artifact: new Uint8Array(INLINE_MAX + 1) });
|
|
426
|
-
|
|
427
|
-
await expect(pushRouted(client, req)).rejects.toThrow(/Upgrade stigmer-server/);
|
|
428
|
-
});
|
|
429
|
-
});
|
|
353
|
+
// The transfer-lane size routing moved down into the SDK (stigmer#701):
|
|
354
|
+
// its pins live in sdk/typescript/src/__tests__/skill.test.ts, exercised
|
|
355
|
+
// through the REAL generated client and error wrapping — the fake client
|
|
356
|
+
// this file previously used threw raw ConnectError, which the SDK's
|
|
357
|
+
// wrapError makes unreachable on the real path.
|