@stigmer/cli 3.12.8 → 3.13.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/commands/agent-exec-flags.d.ts +1 -0
- package/commands/agent-exec-flags.d.ts.map +1 -1
- package/commands/agent-exec-flags.js +3 -1
- package/commands/agent-exec-flags.js.map +1 -1
- package/commands/run.js +3 -0
- package/commands/run.js.map +1 -1
- package/commands/up.d.ts.map +1 -1
- package/commands/up.js +22 -6
- package/commands/up.js.map +1 -1
- package/config/resolve.d.ts +5 -2
- package/config/resolve.d.ts.map +1 -1
- package/config/resolve.js +7 -4
- package/config/resolve.js.map +1 -1
- package/local/constants.d.ts +8 -3
- package/local/constants.d.ts.map +1 -1
- package/local/constants.js +8 -3
- package/local/constants.js.map +1 -1
- package/local/daemon/components.d.ts.map +1 -1
- package/local/daemon/components.js +4 -2
- package/local/daemon/components.js.map +1 -1
- package/local/daemon/env.d.ts +16 -3
- package/local/daemon/env.d.ts.map +1 -1
- package/local/daemon/env.js +16 -3
- package/local/daemon/env.js.map +1 -1
- package/local/daemon/launch.d.ts.map +1 -1
- package/local/daemon/launch.js +5 -4
- package/local/daemon/launch.js.map +1 -1
- package/local/daemon/process.d.ts.map +1 -1
- package/local/daemon/process.js +8 -5
- package/local/daemon/process.js.map +1 -1
- package/local/runtime/index.d.ts +2 -2
- package/local/runtime/index.d.ts.map +1 -1
- package/local/runtime/index.js +4 -2
- package/local/runtime/index.js.map +1 -1
- package/local/runtime/node.d.ts +8 -0
- package/local/runtime/node.d.ts.map +1 -1
- package/local/runtime/node.js +74 -23
- package/local/runtime/node.js.map +1 -1
- package/local/runtime/runner.d.ts +2 -1
- package/local/runtime/runner.d.ts.map +1 -1
- package/local/runtime/runner.js +4 -31
- package/local/runtime/runner.js.map +1 -1
- package/local/runtime/runtimes-install.d.ts +13 -0
- package/local/runtime/runtimes-install.d.ts.map +1 -0
- package/local/runtime/runtimes-install.js +60 -0
- package/local/runtime/runtimes-install.js.map +1 -0
- package/local/runtime/server.d.ts +26 -30
- package/local/runtime/server.d.ts.map +1 -1
- package/local/runtime/server.js +106 -108
- package/local/runtime/server.js.map +1 -1
- package/local/status.d.ts.map +1 -1
- package/local/status.js +4 -2
- package/local/status.js.map +1 -1
- package/local/webconsole/index.d.ts +2 -3
- package/local/webconsole/index.d.ts.map +1 -1
- package/local/webconsole/index.js +24 -10
- package/local/webconsole/index.js.map +1 -1
- package/package.json +5 -5
- package/resources/run/agent-exec.d.ts.map +1 -1
- package/resources/run/agent-exec.js +2 -0
- package/resources/run/agent-exec.js.map +1 -1
- package/resources/run/create.d.ts +2 -1
- package/resources/run/create.d.ts.map +1 -1
- package/resources/run/create.js +21 -3
- package/resources/run/create.js.map +1 -1
- package/resources/run/header.d.ts +9 -1
- package/resources/run/header.d.ts.map +1 -1
- package/resources/run/header.js +3 -0
- package/resources/run/header.js.map +1 -1
- package/resources/run/prepare.d.ts +41 -3
- package/resources/run/prepare.d.ts.map +1 -1
- package/resources/run/prepare.js +53 -16
- package/resources/run/prepare.js.map +1 -1
- package/src/commands/agent-exec-flags.ts +5 -2
- package/src/commands/run.ts +3 -0
- package/src/commands/up.ts +24 -6
- package/src/config/resolve.test.ts +1 -1
- package/src/config/resolve.ts +7 -4
- package/src/local/constants.ts +8 -4
- package/src/local/daemon/components.test.ts +9 -1
- package/src/local/daemon/components.ts +4 -2
- package/src/local/daemon/daemon.integration.test.ts +1 -1
- package/src/local/daemon/env.test.ts +15 -4
- package/src/local/daemon/env.ts +33 -5
- package/src/local/daemon/launch.ts +5 -4
- package/src/local/daemon/process.ts +7 -4
- package/src/local/runtime/index.ts +6 -5
- package/src/local/runtime/node.ts +99 -23
- package/src/local/runtime/runner.ts +5 -36
- package/src/local/runtime/runtime.test.ts +58 -14
- package/src/local/runtime/runtimes-install.test.ts +56 -0
- package/src/local/runtime/runtimes-install.ts +72 -0
- package/src/local/runtime/server.test.ts +178 -114
- package/src/local/runtime/server.ts +149 -129
- package/src/local/status.ts +4 -2
- package/src/local/webconsole/index.ts +27 -10
- package/src/resources/connect/oauth.test.ts +1 -1
- package/src/resources/run/agent-exec.test.ts +25 -0
- package/src/resources/run/agent-exec.ts +2 -0
- package/src/resources/run/create.test.ts +92 -1
- package/src/resources/run/create.ts +25 -6
- package/src/resources/run/header.test.ts +18 -0
- package/src/resources/run/header.ts +11 -1
- package/src/resources/run/prepare.test.ts +247 -5
- package/src/resources/run/prepare.ts +99 -21
- package/src/resources/share.test.ts +2 -2
package/src/local/status.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { join } from "node:path";
|
|
|
12
12
|
import { load as loadConfig } from "../config/config.js";
|
|
13
13
|
import { configPath } from "../config/paths.js";
|
|
14
14
|
import { CommandResult } from "../output/command-result.js";
|
|
15
|
-
import { SERVER_PORT, TEMPORAL_UI_PORT
|
|
15
|
+
import { SERVER_PORT, TEMPORAL_UI_PORT } from "./constants.js";
|
|
16
16
|
import { resolveApiKey, resolveProvider } from "./llm-config.js";
|
|
17
17
|
import { tcpConnects } from "./net/tcp.js";
|
|
18
18
|
import { dataDir } from "./paths.js";
|
|
@@ -144,7 +144,9 @@ function addWebUiSection(result: CommandResult, health: HealthState): void {
|
|
|
144
144
|
if (!temporalRunning && !webRunning) return;
|
|
145
145
|
|
|
146
146
|
const section = result.addSection("Web UI");
|
|
147
|
-
|
|
147
|
+
// The console shares the server's unified port (DD-012) — same origin as
|
|
148
|
+
// the API, served by the server itself.
|
|
149
|
+
if (webRunning) section.field("Console", `http://localhost:${SERVER_PORT}`);
|
|
148
150
|
if (temporalRunning) section.field("Temporal", `http://localhost:${TEMPORAL_UI_PORT}`);
|
|
149
151
|
}
|
|
150
152
|
|
|
@@ -1,13 +1,30 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Web-console availability against the server's unified port.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// :
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// Since DD-012 (oss sub-project console-serving) the SERVER serves the
|
|
4
|
+
// console: the static export ships inside @stigmer/server-slim and the
|
|
5
|
+
// unified port's lane 4 serves it, synthesizing /config.json in-process.
|
|
6
|
+
// The CLI therefore PROBES rather than serves — /config.json answers 200
|
|
7
|
+
// exactly when a console export is bundled with the running server (a
|
|
8
|
+
// dev-tree server without the export falls through to the RPC adapter's
|
|
9
|
+
// 404), so the daemon's component state reports what a browser would
|
|
10
|
+
// actually find. The Go-era embedded console (go:embed on :8234) and the
|
|
11
|
+
// T05/T06 stub this file used to be are both retired by that design.
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
import { SERVER_PORT } from "../constants.js";
|
|
14
|
+
|
|
15
|
+
/** Keep the daemon's settle path snappy: a hung probe must not stall `stigmer up`. */
|
|
16
|
+
const PROBE_TIMEOUT_MS = 3000;
|
|
17
|
+
|
|
18
|
+
/** Whether the running server serves a bundled web console. */
|
|
19
|
+
export async function isWebConsoleAvailable(
|
|
20
|
+
serverPort: number = SERVER_PORT,
|
|
21
|
+
): Promise<boolean> {
|
|
22
|
+
try {
|
|
23
|
+
const response = await fetch(`http://127.0.0.1:${serverPort}/config.json`, {
|
|
24
|
+
signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),
|
|
25
|
+
});
|
|
26
|
+
return response.ok;
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
13
30
|
}
|
|
@@ -127,6 +127,6 @@ describe("runOAuthFlow", () => {
|
|
|
127
127
|
sleep: noopSleep,
|
|
128
128
|
log: () => {},
|
|
129
129
|
});
|
|
130
|
-
expect(openBrowser).toHaveBeenCalledWith("http://localhost:
|
|
130
|
+
expect(openBrowser).toHaveBeenCalledWith("http://localhost:7234/acme/mcp-servers/github");
|
|
131
131
|
});
|
|
132
132
|
});
|
|
@@ -11,6 +11,7 @@ import type { Agent } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
|
11
11
|
import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
12
12
|
import type { AgentExecution } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
13
13
|
import { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
14
|
+
import { Harness } from "@stigmer/protos/ai/stigmer/agentic/session/v1/enum_pb";
|
|
14
15
|
import {
|
|
15
16
|
LocalPathSourceSchema,
|
|
16
17
|
WorkspaceEntrySchema,
|
|
@@ -48,6 +49,7 @@ function makePrepared(overrides: Partial<PreparedRun> = {}): PreparedRun {
|
|
|
48
49
|
mode: "",
|
|
49
50
|
serviceTier: "",
|
|
50
51
|
thinking: "",
|
|
52
|
+
harness: "",
|
|
51
53
|
...overrides,
|
|
52
54
|
};
|
|
53
55
|
}
|
|
@@ -127,4 +129,27 @@ describe("executeResolvedAgent", () => {
|
|
|
127
129
|
expect(sent[0]?.spec?.sessionSpec).toBeUndefined();
|
|
128
130
|
expect(sent[0]?.spec?.agentId).toBe("agt_1");
|
|
129
131
|
});
|
|
132
|
+
|
|
133
|
+
it("threads the resolved harness onto the wire and surfaces it in the header (oss#293)", async () => {
|
|
134
|
+
// The revert-detection seam: if the prepared→create threading is ever
|
|
135
|
+
// dropped, this wire assertion fails — not just a flag-parsing test.
|
|
136
|
+
const { client, creates } = fakeBackend();
|
|
137
|
+
|
|
138
|
+
await executeResolvedAgent({
|
|
139
|
+
agent: makeAgent(),
|
|
140
|
+
prepared: makePrepared({ harness: "cursor" }),
|
|
141
|
+
org: "acme",
|
|
142
|
+
downloadDir: "",
|
|
143
|
+
outputMode: "inline",
|
|
144
|
+
client,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const sent = creates();
|
|
148
|
+
expect(sent).toHaveLength(1);
|
|
149
|
+
expect(sent[0]?.spec?.sessionSpec?.harness).toBe(Harness.CURSOR);
|
|
150
|
+
// D2 visibility: a cursor session must announce itself before streaming —
|
|
151
|
+
// whether the flag or the account preference selected it.
|
|
152
|
+
expect(stderrLines.join("")).toContain("Harness:");
|
|
153
|
+
expect(stderrLines.join("")).toContain("Cursor");
|
|
154
|
+
});
|
|
130
155
|
});
|
|
@@ -45,6 +45,7 @@ export async function executeResolvedAgent(input: ResolvedAgentExecInput): Promi
|
|
|
45
45
|
serviceTier: prepared.serviceTier,
|
|
46
46
|
thinking: prepared.thinking,
|
|
47
47
|
autoApproveAll: prepared.autoApproveAll,
|
|
48
|
+
harness: prepared.harness,
|
|
48
49
|
});
|
|
49
50
|
|
|
50
51
|
// The backend owns the canonical session id: it creates the session and
|
|
@@ -56,6 +57,7 @@ export async function executeResolvedAgent(input: ResolvedAgentExecInput): Promi
|
|
|
56
57
|
sessionId,
|
|
57
58
|
model: prepared.model,
|
|
58
59
|
mode: prepared.mode,
|
|
60
|
+
harness: prepared.harness,
|
|
59
61
|
workspaces: workspaceNames(prepared.workspaceEntries),
|
|
60
62
|
};
|
|
61
63
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { describe, expect, it } from "vitest";
|
|
7
7
|
import { InteractionMode, ServiceTier, ThinkingMode } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
8
8
|
import { create } from "@bufbuild/protobuf";
|
|
9
|
+
import { Harness } from "@stigmer/protos/ai/stigmer/agentic/session/v1/enum_pb";
|
|
9
10
|
import {
|
|
10
11
|
LocalPathSourceSchema,
|
|
11
12
|
WorkspaceEntrySchema,
|
|
@@ -43,6 +44,7 @@ describe("createAgentExecution", () => {
|
|
|
43
44
|
serviceTier: "",
|
|
44
45
|
thinking: "",
|
|
45
46
|
autoApproveAll: true,
|
|
47
|
+
harness: "",
|
|
46
48
|
});
|
|
47
49
|
|
|
48
50
|
expect(exec.kind).toBe("AgentExecution");
|
|
@@ -72,6 +74,7 @@ describe("createAgentExecution", () => {
|
|
|
72
74
|
serviceTier: "",
|
|
73
75
|
thinking: "",
|
|
74
76
|
autoApproveAll: false,
|
|
77
|
+
harness: "",
|
|
75
78
|
});
|
|
76
79
|
expect(exec.spec?.message).toBe("hi");
|
|
77
80
|
expect(exec.spec?.executionConfig).toBeUndefined();
|
|
@@ -92,6 +95,7 @@ describe("createAgentExecution", () => {
|
|
|
92
95
|
serviceTier: "fast",
|
|
93
96
|
thinking: "",
|
|
94
97
|
autoApproveAll: false,
|
|
98
|
+
harness: "",
|
|
95
99
|
});
|
|
96
100
|
expect(exec.spec?.executionConfig?.serviceTier).toBe(ServiceTier.FAST);
|
|
97
101
|
});
|
|
@@ -113,6 +117,7 @@ describe("createAgentExecution", () => {
|
|
|
113
117
|
serviceTier: "standard",
|
|
114
118
|
thinking: "",
|
|
115
119
|
autoApproveAll: false,
|
|
120
|
+
harness: "",
|
|
116
121
|
});
|
|
117
122
|
expect(exec.spec?.executionConfig?.serviceTier).toBe(ServiceTier.STANDARD);
|
|
118
123
|
});
|
|
@@ -132,6 +137,7 @@ describe("createAgentExecution", () => {
|
|
|
132
137
|
serviceTier: "",
|
|
133
138
|
thinking: "enabled",
|
|
134
139
|
autoApproveAll: false,
|
|
140
|
+
harness: "",
|
|
135
141
|
});
|
|
136
142
|
expect(exec.spec?.executionConfig?.thinkingMode).toBe(ThinkingMode.ENABLED);
|
|
137
143
|
});
|
|
@@ -153,6 +159,7 @@ describe("createAgentExecution", () => {
|
|
|
153
159
|
serviceTier: "",
|
|
154
160
|
thinking: "disabled",
|
|
155
161
|
autoApproveAll: false,
|
|
162
|
+
harness: "",
|
|
156
163
|
});
|
|
157
164
|
expect(exec.spec?.executionConfig?.thinkingMode).toBe(ThinkingMode.DISABLED);
|
|
158
165
|
});
|
|
@@ -172,6 +179,7 @@ describe("createAgentExecution", () => {
|
|
|
172
179
|
serviceTier: "",
|
|
173
180
|
thinking: "",
|
|
174
181
|
autoApproveAll: false,
|
|
182
|
+
harness: "",
|
|
175
183
|
});
|
|
176
184
|
expect(exec.spec?.sessionId).toBe("ses_1");
|
|
177
185
|
expect(exec.spec?.executionConfig?.interactionMode).toBe(InteractionMode.UNSPECIFIED);
|
|
@@ -199,6 +207,7 @@ describe("createAgentExecution", () => {
|
|
|
199
207
|
serviceTier: "",
|
|
200
208
|
thinking: "",
|
|
201
209
|
autoApproveAll: false,
|
|
210
|
+
harness: "",
|
|
202
211
|
});
|
|
203
212
|
|
|
204
213
|
expect(exec.spec?.sessionId).toBe("");
|
|
@@ -206,9 +215,16 @@ describe("createAgentExecution", () => {
|
|
|
206
215
|
// Subject stays empty so the server defaults its sentinel and the async
|
|
207
216
|
// title activity generates a real one.
|
|
208
217
|
expect(exec.spec?.sessionSpec?.subject).toBe("");
|
|
218
|
+
// No harness opinion: the field stays UNSPECIFIED so the server default
|
|
219
|
+
// (native) applies — a workspace-only run is byte-identical to before
|
|
220
|
+
// the --harness flag existed.
|
|
221
|
+
expect(exec.spec?.sessionSpec?.harness).toBe(Harness.UNSPECIFIED);
|
|
209
222
|
});
|
|
210
223
|
|
|
211
|
-
it("omits session_spec when there are no workspace entries", async () => {
|
|
224
|
+
it("omits session_spec when there are no workspace entries and no harness (wire-shape regression pin)", async () => {
|
|
225
|
+
// The pre---harness wire shape for a plain run: NO embedded session_spec
|
|
226
|
+
// at all. If this pin fails, plain runs have started carrying a spec they
|
|
227
|
+
// never carried before — a silent contract change, not a feature.
|
|
212
228
|
const { fn } = fakeController();
|
|
213
229
|
const exec = await createAgentExecution(fn, {
|
|
214
230
|
agentId: "agt_1",
|
|
@@ -223,9 +239,84 @@ describe("createAgentExecution", () => {
|
|
|
223
239
|
serviceTier: "",
|
|
224
240
|
thinking: "",
|
|
225
241
|
autoApproveAll: false,
|
|
242
|
+
harness: "",
|
|
226
243
|
});
|
|
227
244
|
expect(exec.spec?.sessionSpec).toBeUndefined();
|
|
228
245
|
});
|
|
246
|
+
|
|
247
|
+
it("stamps cursor harness on a session_spec created just for it (oss#293)", async () => {
|
|
248
|
+
const { fn } = fakeController();
|
|
249
|
+
const exec = await createAgentExecution(fn, {
|
|
250
|
+
agentId: "agt_1",
|
|
251
|
+
orgId: "acme",
|
|
252
|
+
message: "hi",
|
|
253
|
+
runtimeEnv: {},
|
|
254
|
+
attachments: [],
|
|
255
|
+
workspaceFileRefs: [],
|
|
256
|
+
workspaceEntries: [],
|
|
257
|
+
model: "",
|
|
258
|
+
mode: "",
|
|
259
|
+
serviceTier: "",
|
|
260
|
+
thinking: "",
|
|
261
|
+
autoApproveAll: false,
|
|
262
|
+
harness: "cursor",
|
|
263
|
+
});
|
|
264
|
+
// Harness alone justifies the embedded spec: the server clones it onto
|
|
265
|
+
// the auto-created session (the one-call bootstrap contract).
|
|
266
|
+
expect(exec.spec?.sessionSpec?.harness).toBe(Harness.CURSOR);
|
|
267
|
+
expect(exec.spec?.sessionSpec?.workspaceEntries).toEqual([]);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("stamps an explicit native harness rather than leaving it unspecified", async () => {
|
|
271
|
+
// "native" may be a deliberate per-run escape from the account's
|
|
272
|
+
// default_harness preference — it must survive on the wire so it beats
|
|
273
|
+
// any future change to the server-side default (D3).
|
|
274
|
+
const { fn } = fakeController();
|
|
275
|
+
const exec = await createAgentExecution(fn, {
|
|
276
|
+
agentId: "agt_1",
|
|
277
|
+
orgId: "acme",
|
|
278
|
+
message: "hi",
|
|
279
|
+
runtimeEnv: {},
|
|
280
|
+
attachments: [],
|
|
281
|
+
workspaceFileRefs: [],
|
|
282
|
+
workspaceEntries: [],
|
|
283
|
+
model: "",
|
|
284
|
+
mode: "",
|
|
285
|
+
serviceTier: "",
|
|
286
|
+
thinking: "",
|
|
287
|
+
autoApproveAll: false,
|
|
288
|
+
harness: "native",
|
|
289
|
+
});
|
|
290
|
+
expect(exec.spec?.sessionSpec?.harness).toBe(Harness.NATIVE);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("carries harness and workspace entries together on one session_spec", async () => {
|
|
294
|
+
const entry = create(WorkspaceEntrySchema, {
|
|
295
|
+
name: "repo",
|
|
296
|
+
source: create(WorkspaceSourceSchema, {
|
|
297
|
+
source: { case: "localPath", value: create(LocalPathSourceSchema, { path: "/home/user/repo" }) },
|
|
298
|
+
}),
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const { fn } = fakeController();
|
|
302
|
+
const exec = await createAgentExecution(fn, {
|
|
303
|
+
agentId: "agt_1",
|
|
304
|
+
orgId: "acme",
|
|
305
|
+
message: "hi",
|
|
306
|
+
runtimeEnv: {},
|
|
307
|
+
attachments: [],
|
|
308
|
+
workspaceFileRefs: [],
|
|
309
|
+
workspaceEntries: [entry],
|
|
310
|
+
model: "",
|
|
311
|
+
mode: "",
|
|
312
|
+
serviceTier: "",
|
|
313
|
+
thinking: "",
|
|
314
|
+
autoApproveAll: false,
|
|
315
|
+
harness: "cursor",
|
|
316
|
+
});
|
|
317
|
+
expect(exec.spec?.sessionSpec?.harness).toBe(Harness.CURSOR);
|
|
318
|
+
expect(exec.spec?.sessionSpec?.workspaceEntries).toEqual([entry]);
|
|
319
|
+
});
|
|
229
320
|
});
|
|
230
321
|
|
|
231
322
|
describe("createWorkflowExecution", () => {
|
|
@@ -18,7 +18,8 @@ import {
|
|
|
18
18
|
ExecutionConfigSchema,
|
|
19
19
|
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/spec_pb";
|
|
20
20
|
import type { ExecutionValue } from "@stigmer/protos/ai/stigmer/agentic/executioncontext/v1/spec_pb";
|
|
21
|
-
import {
|
|
21
|
+
import { Harness } from "@stigmer/protos/ai/stigmer/agentic/session/v1/enum_pb";
|
|
22
|
+
import { type SessionSpec, SessionSpecSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/spec_pb";
|
|
22
23
|
import type { WorkspaceEntry } from "@stigmer/protos/ai/stigmer/agentic/session/v1/workspace_pb";
|
|
23
24
|
import {
|
|
24
25
|
type WorkflowExecution,
|
|
@@ -29,7 +30,7 @@ import { WorkflowExecutionSpecSchema } from "@stigmer/protos/ai/stigmer/agentic/
|
|
|
29
30
|
import { ExecutionValueSchema } from "@stigmer/protos/ai/stigmer/agentic/executioncontext/v1/spec_pb";
|
|
30
31
|
import { ApiResourceMetadataSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
|
|
31
32
|
import type { RuntimeEnv } from "./env.js";
|
|
32
|
-
import type { RunMode, ServiceTierFlag, ThinkingFlag } from "./prepare.js";
|
|
33
|
+
import type { HarnessFlag, RunMode, ServiceTierFlag, ThinkingFlag } from "./prepare.js";
|
|
33
34
|
|
|
34
35
|
const API_VERSION = "agentic.stigmer.ai/v1";
|
|
35
36
|
|
|
@@ -60,6 +61,7 @@ export interface CreateAgentExecutionInput {
|
|
|
60
61
|
readonly serviceTier: ServiceTierFlag;
|
|
61
62
|
readonly thinking: ThinkingFlag;
|
|
62
63
|
readonly autoApproveAll: boolean;
|
|
64
|
+
readonly harness: HarnessFlag;
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
/** Create an agent execution. Mirrors Go's createAgentExecution. */
|
|
@@ -81,10 +83,7 @@ export async function createAgentExecution(
|
|
|
81
83
|
agentId: input.agentId ?? "",
|
|
82
84
|
// Subject is left empty: the server defaults its sentinel and the async
|
|
83
85
|
// title activity replaces it, same as any auto-created session.
|
|
84
|
-
sessionSpec:
|
|
85
|
-
input.workspaceEntries.length > 0
|
|
86
|
-
? create(SessionSpecSchema, { workspaceEntries: [...input.workspaceEntries] })
|
|
87
|
-
: undefined,
|
|
86
|
+
sessionSpec: buildSessionSpec(input.workspaceEntries, input.harness),
|
|
88
87
|
executionConfig: buildExecutionConfig(input.model, input.mode, input.serviceTier, input.thinking),
|
|
89
88
|
}),
|
|
90
89
|
});
|
|
@@ -121,6 +120,26 @@ export async function createWorkflowExecution(
|
|
|
121
120
|
return controller(WorkflowExecutionCommandController).create(execution);
|
|
122
121
|
}
|
|
123
122
|
|
|
123
|
+
// Build the embedded session spec for the one-call bootstrap
|
|
124
|
+
// (stigmer/stigmer#249), or undefined when there is nothing to carry — the
|
|
125
|
+
// pre-existing wire shape for a plain run must stay byte-identical. A resolved
|
|
126
|
+
// harness is stamped explicitly, including "native": the value may be a
|
|
127
|
+
// deliberate per-run escape from the account's default_harness preference, so
|
|
128
|
+
// it must survive any future change to the server-side default. Empty means
|
|
129
|
+
// "no opinion" and stays off the wire (server defaults to native). The server
|
|
130
|
+
// clones this spec onto the auto-created session and then clears it from the
|
|
131
|
+
// persisted execution — the Session resource stays the single source of truth.
|
|
132
|
+
function buildSessionSpec(
|
|
133
|
+
workspaceEntries: readonly WorkspaceEntry[],
|
|
134
|
+
harness: HarnessFlag,
|
|
135
|
+
): SessionSpec | undefined {
|
|
136
|
+
if (workspaceEntries.length === 0 && harness === "") return undefined;
|
|
137
|
+
const spec = create(SessionSpecSchema, { workspaceEntries: [...workspaceEntries] });
|
|
138
|
+
if (harness === "cursor") spec.harness = Harness.CURSOR;
|
|
139
|
+
else if (harness === "native") spec.harness = Harness.NATIVE;
|
|
140
|
+
return spec;
|
|
141
|
+
}
|
|
142
|
+
|
|
124
143
|
// Build ExecutionConfig, or undefined when no flag is set so the backend
|
|
125
144
|
// applies its defaults. Mirrors Go's buildExecutionConfig (only "plan" maps to a
|
|
126
145
|
// non-default InteractionMode; "agent"/"" leave it unspecified). An explicit
|
|
@@ -31,6 +31,24 @@ describe("renderSessionHeader", () => {
|
|
|
31
31
|
expect(agent.text()).not.toContain("Mode:");
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
+
it("surfaces the cursor harness but not native/default (oss#293 D2 visibility)", () => {
|
|
35
|
+
// A cursor session may have been selected by the account preference, not
|
|
36
|
+
// a flag — the header is the CLI's only pre-stream channel to say so.
|
|
37
|
+
const cursor = capture();
|
|
38
|
+
renderSessionHeader(cursor.out, { agentName: "", sessionId: "s", model: "", mode: "", harness: "cursor", workspaces: [] });
|
|
39
|
+
expect(cursor.text()).toContain("Harness:");
|
|
40
|
+
expect(cursor.text()).toContain("Cursor");
|
|
41
|
+
|
|
42
|
+
// Native (explicit or defaulted) stays implicit, like agent mode.
|
|
43
|
+
const native = capture();
|
|
44
|
+
renderSessionHeader(native.out, { agentName: "", sessionId: "s", model: "", mode: "", harness: "native", workspaces: [] });
|
|
45
|
+
expect(native.text()).not.toContain("Harness:");
|
|
46
|
+
|
|
47
|
+
const unset = capture();
|
|
48
|
+
renderSessionHeader(unset.out, { agentName: "", sessionId: "s", model: "", mode: "", workspaces: [] });
|
|
49
|
+
expect(unset.text()).not.toContain("Harness:");
|
|
50
|
+
});
|
|
51
|
+
|
|
34
52
|
it("indents extra workspaces under the first", () => {
|
|
35
53
|
const { out, text } = capture();
|
|
36
54
|
renderSessionHeader(out, {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import type { WorkspaceEntry } from "@stigmer/protos/ai/stigmer/agentic/session/v1/workspace_pb";
|
|
13
13
|
import { shouldColorize, styler } from "../../output/style.js";
|
|
14
|
-
import type { RunMode } from "./prepare.js";
|
|
14
|
+
import type { HarnessFlag, RunMode } from "./prepare.js";
|
|
15
15
|
|
|
16
16
|
export interface SessionHeaderInfo {
|
|
17
17
|
readonly agentName: string;
|
|
@@ -20,6 +20,14 @@ export interface SessionHeaderInfo {
|
|
|
20
20
|
readonly subject?: string;
|
|
21
21
|
readonly model: string;
|
|
22
22
|
readonly mode: RunMode;
|
|
23
|
+
/**
|
|
24
|
+
* Resolved harness for the session. Surfaced only when it is "cursor":
|
|
25
|
+
* a harness switch changes the toolset, conversation-state model, and
|
|
26
|
+
* billing tier, and it may come from the account preference rather than
|
|
27
|
+
* a flag — a cursor session must never start silently (the CLI
|
|
28
|
+
* counterpart of the web composer's harness selector).
|
|
29
|
+
*/
|
|
30
|
+
readonly harness?: HarnessFlag;
|
|
23
31
|
readonly workspaces: readonly string[];
|
|
24
32
|
}
|
|
25
33
|
|
|
@@ -39,6 +47,8 @@ export function renderSessionHeader(out: { write(chunk: string): unknown; isTTY?
|
|
|
39
47
|
if (info.sessionId !== "") lines.push(row(s, "Session", info.sessionId));
|
|
40
48
|
if ((info.subject ?? "") !== "") lines.push(row(s, "Subject", info.subject ?? ""));
|
|
41
49
|
if (info.model !== "") lines.push(row(s, "Model", info.model));
|
|
50
|
+
// Only "cursor" is surfaced; native/"" is the default and stays implicit.
|
|
51
|
+
if (info.harness === "cursor") lines.push(row(s, "Harness", "Cursor"));
|
|
42
52
|
// Only "plan" is surfaced; "agent"/"" is the default and stays implicit.
|
|
43
53
|
if (info.mode === "plan") lines.push(row(s, "Mode", "Plan (read-only)"));
|
|
44
54
|
if (info.workspaces.length > 0) {
|