@stigmer/cli 3.14.1 → 3.15.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/client/client.d.ts +20 -1
- package/client/client.d.ts.map +1 -1
- package/client/client.js +32 -5
- package/client/client.js.map +1 -1
- package/commands/auth/index.d.ts.map +1 -1
- package/commands/auth/index.js +7 -36
- package/commands/auth/index.js.map +1 -1
- package/commands/auth/login.d.ts +13 -0
- package/commands/auth/login.d.ts.map +1 -0
- package/commands/auth/login.js +58 -0
- package/commands/auth/login.js.map +1 -0
- package/commands/auth/whoami.d.ts +18 -0
- package/commands/auth/whoami.d.ts.map +1 -0
- package/commands/auth/whoami.js +58 -0
- package/commands/auth/whoami.js.map +1 -0
- package/commands/draft.d.ts.map +1 -1
- package/commands/draft.js +24 -8
- package/commands/draft.js.map +1 -1
- package/commands/run.d.ts.map +1 -1
- package/commands/run.js +15 -6
- package/commands/run.js.map +1 -1
- package/package.json +5 -5
- package/resources/run/prepare.d.ts +10 -8
- package/resources/run/prepare.d.ts.map +1 -1
- package/resources/run/prepare.js +18 -4
- package/resources/run/prepare.js.map +1 -1
- package/src/client/client.test.ts +89 -1
- package/src/client/client.ts +82 -9
- package/src/commands/auth/index.ts +7 -56
- package/src/commands/auth/login.test.ts +52 -0
- package/src/commands/auth/login.ts +70 -0
- package/src/commands/auth/whoami.test.ts +96 -0
- package/src/commands/auth/whoami.ts +87 -0
- package/src/commands/draft.ts +85 -26
- package/src/commands/run.ts +77 -35
- package/src/resources/run/prepare.test.ts +28 -26
- package/src/resources/run/prepare.ts +48 -18
|
@@ -149,8 +149,8 @@ function countingClient(preferences: StubPreferences): { client: Stigmer; calls:
|
|
|
149
149
|
return { client, calls: () => calls };
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
/** prepareAgentExec options as `run` passes them:
|
|
153
|
-
const
|
|
152
|
+
/** prepareAgentExec options as `run` passes them: the kind is served here + harness opt-in. */
|
|
153
|
+
const SERVED_RUN_OPTIONS = { accountPreferencesAvailable: true, applyAccountHarnessDefault: true } as const;
|
|
154
154
|
|
|
155
155
|
describe("prepareAgentExec env injection", () => {
|
|
156
156
|
it("injects STIGMER_ORG_ID when absent", async () => {
|
|
@@ -193,7 +193,7 @@ describe("prepareAgentExec account-preference model fill (oss#293 Phase 1.5)", (
|
|
|
193
193
|
clientWithPreference("claude-sonnet-4.6"),
|
|
194
194
|
"acme",
|
|
195
195
|
undefined,
|
|
196
|
-
{
|
|
196
|
+
{ accountPreferencesAvailable: true },
|
|
197
197
|
);
|
|
198
198
|
expect(prepared.model).toBe("claude-sonnet-4.6");
|
|
199
199
|
});
|
|
@@ -204,20 +204,22 @@ describe("prepareAgentExec account-preference model fill (oss#293 Phase 1.5)", (
|
|
|
204
204
|
clientWithPreference("claude-sonnet-4.6"),
|
|
205
205
|
"acme",
|
|
206
206
|
undefined,
|
|
207
|
-
{
|
|
207
|
+
{ accountPreferencesAvailable: true },
|
|
208
208
|
);
|
|
209
209
|
expect(prepared.model).toBe("gpt-5.3");
|
|
210
210
|
});
|
|
211
211
|
|
|
212
|
-
it("never consults identity
|
|
213
|
-
// The failing stub doubles as a call detector:
|
|
214
|
-
// attempt whoAmI, so a rejecting
|
|
212
|
+
it("never consults identity where the kind is not served", async () => {
|
|
213
|
+
// The failing stub doubles as a call detector: a backend that does not
|
|
214
|
+
// serve identity accounts must not even attempt whoAmI, so a rejecting
|
|
215
|
+
// client cannot affect the result. (20260911.11 A3: the gate is the
|
|
216
|
+
// kind's tier, not the backend type — client/edition.ts resourceServedOn.)
|
|
215
217
|
const prepared = await prepareAgentExec(
|
|
216
218
|
BASE_FLAGS,
|
|
217
219
|
clientWithFailingWhoAmI(),
|
|
218
220
|
"acme",
|
|
219
221
|
undefined,
|
|
220
|
-
{
|
|
222
|
+
{ accountPreferencesAvailable: false },
|
|
221
223
|
);
|
|
222
224
|
expect(prepared.model).toBe("");
|
|
223
225
|
});
|
|
@@ -228,7 +230,7 @@ describe("prepareAgentExec account-preference model fill (oss#293 Phase 1.5)", (
|
|
|
228
230
|
clientWithFailingWhoAmI(),
|
|
229
231
|
"acme",
|
|
230
232
|
undefined,
|
|
231
|
-
{
|
|
233
|
+
{ accountPreferencesAvailable: true },
|
|
232
234
|
);
|
|
233
235
|
expect(prepared.model).toBe("");
|
|
234
236
|
});
|
|
@@ -239,7 +241,7 @@ describe("prepareAgentExec account-preference model fill (oss#293 Phase 1.5)", (
|
|
|
239
241
|
clientWithPreference(""),
|
|
240
242
|
"acme",
|
|
241
243
|
undefined,
|
|
242
|
-
{
|
|
244
|
+
{ accountPreferencesAvailable: true },
|
|
243
245
|
);
|
|
244
246
|
expect(prepared.model).toBe("");
|
|
245
247
|
});
|
|
@@ -252,7 +254,7 @@ describe("prepareAgentExec harness resolution (oss#293)", () => {
|
|
|
252
254
|
clientWithPreferences({ defaultHarness: "cursor" }),
|
|
253
255
|
"acme",
|
|
254
256
|
undefined,
|
|
255
|
-
|
|
257
|
+
SERVED_RUN_OPTIONS,
|
|
256
258
|
);
|
|
257
259
|
expect(prepared.harness).toBe("cursor");
|
|
258
260
|
});
|
|
@@ -265,7 +267,7 @@ describe("prepareAgentExec harness resolution (oss#293)", () => {
|
|
|
265
267
|
clientWithPreferences({ defaultHarness: "cursor" }),
|
|
266
268
|
"acme",
|
|
267
269
|
undefined,
|
|
268
|
-
|
|
270
|
+
SERVED_RUN_OPTIONS,
|
|
269
271
|
);
|
|
270
272
|
expect(prepared.harness).toBe("native");
|
|
271
273
|
});
|
|
@@ -279,7 +281,7 @@ describe("prepareAgentExec harness resolution (oss#293)", () => {
|
|
|
279
281
|
clientWithPreferences({ defaultHarness: "cursor" }),
|
|
280
282
|
"acme",
|
|
281
283
|
undefined,
|
|
282
|
-
{
|
|
284
|
+
{ accountPreferencesAvailable: true },
|
|
283
285
|
);
|
|
284
286
|
expect(prepared.harness).toBe("");
|
|
285
287
|
});
|
|
@@ -290,7 +292,7 @@ describe("prepareAgentExec harness resolution (oss#293)", () => {
|
|
|
290
292
|
STUB_CLIENT,
|
|
291
293
|
"acme",
|
|
292
294
|
undefined,
|
|
293
|
-
{
|
|
295
|
+
{ accountPreferencesAvailable: false },
|
|
294
296
|
);
|
|
295
297
|
expect(prepared.harness).toBe("cursor");
|
|
296
298
|
});
|
|
@@ -303,7 +305,7 @@ describe("prepareAgentExec harness resolution (oss#293)", () => {
|
|
|
303
305
|
clientWithPreferences({ defaultHarness: "devin" }),
|
|
304
306
|
"acme",
|
|
305
307
|
undefined,
|
|
306
|
-
|
|
308
|
+
SERVED_RUN_OPTIONS,
|
|
307
309
|
);
|
|
308
310
|
expect(prepared.harness).toBe("");
|
|
309
311
|
});
|
|
@@ -314,7 +316,7 @@ describe("prepareAgentExec harness resolution (oss#293)", () => {
|
|
|
314
316
|
clientWithFailingWhoAmI(),
|
|
315
317
|
"acme",
|
|
316
318
|
undefined,
|
|
317
|
-
|
|
319
|
+
SERVED_RUN_OPTIONS,
|
|
318
320
|
);
|
|
319
321
|
expect(prepared.harness).toBe("");
|
|
320
322
|
expect(prepared.model).toBe("");
|
|
@@ -327,7 +329,7 @@ describe("prepareAgentExec harness resolution (oss#293)", () => {
|
|
|
327
329
|
clientWithFailingWhoAmI(),
|
|
328
330
|
"acme",
|
|
329
331
|
undefined,
|
|
330
|
-
{
|
|
332
|
+
{ accountPreferencesAvailable: false, applyAccountHarnessDefault: true },
|
|
331
333
|
);
|
|
332
334
|
expect(prepared.harness).toBe("");
|
|
333
335
|
});
|
|
@@ -338,7 +340,7 @@ describe("prepareAgentExec harness resolution (oss#293)", () => {
|
|
|
338
340
|
clientWithPreferences({}),
|
|
339
341
|
"acme",
|
|
340
342
|
undefined,
|
|
341
|
-
|
|
343
|
+
SERVED_RUN_OPTIONS,
|
|
342
344
|
);
|
|
343
345
|
expect(prepared.harness).toBe("");
|
|
344
346
|
});
|
|
@@ -356,7 +358,7 @@ describe("prepareAgentExec harness-aware model fill (oss#293)", () => {
|
|
|
356
358
|
clientWithPreferences(BOTH_MODELS),
|
|
357
359
|
"acme",
|
|
358
360
|
undefined,
|
|
359
|
-
|
|
361
|
+
SERVED_RUN_OPTIONS,
|
|
360
362
|
);
|
|
361
363
|
expect(prepared.model).toBe("composer-2.5");
|
|
362
364
|
});
|
|
@@ -367,7 +369,7 @@ describe("prepareAgentExec harness-aware model fill (oss#293)", () => {
|
|
|
367
369
|
clientWithPreferences({ ...BOTH_MODELS, defaultHarness: "cursor" }),
|
|
368
370
|
"acme",
|
|
369
371
|
undefined,
|
|
370
|
-
|
|
372
|
+
SERVED_RUN_OPTIONS,
|
|
371
373
|
);
|
|
372
374
|
expect(prepared.harness).toBe("cursor");
|
|
373
375
|
expect(prepared.model).toBe("composer-2.5");
|
|
@@ -379,7 +381,7 @@ describe("prepareAgentExec harness-aware model fill (oss#293)", () => {
|
|
|
379
381
|
clientWithPreferences(BOTH_MODELS),
|
|
380
382
|
"acme",
|
|
381
383
|
undefined,
|
|
382
|
-
|
|
384
|
+
SERVED_RUN_OPTIONS,
|
|
383
385
|
);
|
|
384
386
|
expect(explicit.model).toBe("claude-sonnet-4.6");
|
|
385
387
|
|
|
@@ -388,7 +390,7 @@ describe("prepareAgentExec harness-aware model fill (oss#293)", () => {
|
|
|
388
390
|
clientWithPreferences(BOTH_MODELS),
|
|
389
391
|
"acme",
|
|
390
392
|
undefined,
|
|
391
|
-
|
|
393
|
+
SERVED_RUN_OPTIONS,
|
|
392
394
|
);
|
|
393
395
|
expect(unset.model).toBe("claude-sonnet-4.6");
|
|
394
396
|
});
|
|
@@ -399,7 +401,7 @@ describe("prepareAgentExec harness-aware model fill (oss#293)", () => {
|
|
|
399
401
|
clientWithPreferences(BOTH_MODELS),
|
|
400
402
|
"acme",
|
|
401
403
|
undefined,
|
|
402
|
-
|
|
404
|
+
SERVED_RUN_OPTIONS,
|
|
403
405
|
);
|
|
404
406
|
expect(prepared.model).toBe("gpt-5.3");
|
|
405
407
|
});
|
|
@@ -412,7 +414,7 @@ describe("prepareAgentExec harness-aware model fill (oss#293)", () => {
|
|
|
412
414
|
clientWithPreferences(BOTH_MODELS),
|
|
413
415
|
"acme",
|
|
414
416
|
undefined,
|
|
415
|
-
{
|
|
417
|
+
{ accountPreferencesAvailable: true },
|
|
416
418
|
);
|
|
417
419
|
expect(prepared.harness).toBe("cursor");
|
|
418
420
|
expect(prepared.model).toBe("composer-2.5");
|
|
@@ -420,7 +422,7 @@ describe("prepareAgentExec harness-aware model fill (oss#293)", () => {
|
|
|
420
422
|
|
|
421
423
|
it("serves both fills from one whoAmI round trip, and none when both flags are explicit", async () => {
|
|
422
424
|
const single = countingClient({ ...BOTH_MODELS, defaultHarness: "cursor" });
|
|
423
|
-
const filled = await prepareAgentExec(BASE_FLAGS, single.client, "acme", undefined,
|
|
425
|
+
const filled = await prepareAgentExec(BASE_FLAGS, single.client, "acme", undefined, SERVED_RUN_OPTIONS);
|
|
424
426
|
expect(filled.harness).toBe("cursor");
|
|
425
427
|
expect(filled.model).toBe("composer-2.5");
|
|
426
428
|
expect(single.calls(), "harness + model fills share one whoAmI").toBe(1);
|
|
@@ -431,7 +433,7 @@ describe("prepareAgentExec harness-aware model fill (oss#293)", () => {
|
|
|
431
433
|
skipped.client,
|
|
432
434
|
"acme",
|
|
433
435
|
undefined,
|
|
434
|
-
|
|
436
|
+
SERVED_RUN_OPTIONS,
|
|
435
437
|
);
|
|
436
438
|
expect(explicit.harness).toBe("native");
|
|
437
439
|
expect(explicit.model).toBe("gpt-5.3");
|
|
@@ -93,18 +93,20 @@ export interface PreparedRun {
|
|
|
93
93
|
/** Optional behavior switches for {@link prepareAgentExec}. */
|
|
94
94
|
export interface PrepareAgentExecOptions {
|
|
95
95
|
/**
|
|
96
|
-
* When `true`
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* `
|
|
101
|
-
*
|
|
96
|
+
* When `true` and `--model` is omitted, the model is filled from the
|
|
97
|
+
* caller's account preference (`IdentityAccountPreferences.default_*_model`
|
|
98
|
+
* for the resolved harness) via `whoAmI()`. The caller answers this from
|
|
99
|
+
* the connected server's edition and the kind's tier —
|
|
100
|
+
* `client.isResourceAvailable(ApiResourceKind.identity_account)` — never
|
|
101
|
+
* from the config's backend type (20260911.11 A3): a server that does not
|
|
102
|
+
* serve identity accounts is not even asked, and the omitted model keeps
|
|
103
|
+
* resolving to the platform default.
|
|
102
104
|
*/
|
|
103
|
-
readonly
|
|
105
|
+
readonly accountPreferencesAvailable?: boolean;
|
|
104
106
|
/**
|
|
105
107
|
* When `true` and `--harness` is omitted, the harness is filled from the
|
|
106
108
|
* caller's account preference (`IdentityAccountPreferences.default_harness`)
|
|
107
|
-
*
|
|
109
|
+
* where the kind is served. `run` opts in; `draft` deliberately does not: draft executes the
|
|
108
110
|
* seedpack's system creator agents (a utility flow, not the user's own
|
|
109
111
|
* conversation), which are built and exercised on the native harness —
|
|
110
112
|
* silently rerouting them onto the cursor engine (different toolset,
|
|
@@ -133,27 +135,38 @@ export async function prepareAgentExec(
|
|
|
133
135
|
validateHarness(flags.harness);
|
|
134
136
|
|
|
135
137
|
// Layered seeds (oss#293, DD-003): explicit flag > account preference
|
|
136
|
-
// (
|
|
138
|
+
// (where the server serves identity accounts) > platform default. Harness resolves first because the model
|
|
137
139
|
// fill is harness-aware: a cursor session fills from default_cursor_model,
|
|
138
140
|
// everything else from default_native_model. `run` and `draft` always
|
|
139
141
|
// create a NEW session (threading lives in `resume`, which does not pass
|
|
140
142
|
// through here), so neither fill can ever contradict an existing session's
|
|
141
143
|
// immutable harness. One whoAmI round trip serves both fills, and it is
|
|
142
144
|
// skipped entirely when explicit flags leave nothing to fill.
|
|
143
|
-
const wantsHarnessFill =
|
|
145
|
+
const wantsHarnessFill =
|
|
146
|
+
flags.harness === "" && options?.applyAccountHarnessDefault === true;
|
|
144
147
|
const wantsModelFill = flags.model === "";
|
|
145
148
|
const accountDefaults =
|
|
146
|
-
options?.
|
|
149
|
+
options?.accountPreferencesAvailable === true &&
|
|
150
|
+
(wantsHarnessFill || wantsModelFill)
|
|
147
151
|
? await resolveAccountExecutionDefaults(client)
|
|
148
152
|
: NO_ACCOUNT_DEFAULTS;
|
|
149
|
-
const harness =
|
|
153
|
+
const harness =
|
|
154
|
+
flags.harness !== ""
|
|
155
|
+
? flags.harness
|
|
156
|
+
: wantsHarnessFill
|
|
157
|
+
? accountDefaults.harness
|
|
158
|
+
: "";
|
|
150
159
|
const model = wantsModelFill
|
|
151
160
|
? harness === "cursor"
|
|
152
161
|
? accountDefaults.cursorModel
|
|
153
162
|
: accountDefaults.nativeModel
|
|
154
163
|
: flags.model;
|
|
155
164
|
|
|
156
|
-
const workspaceEntries = parseWorkspaceEntries(
|
|
165
|
+
const workspaceEntries = parseWorkspaceEntries(
|
|
166
|
+
flags.workspace,
|
|
167
|
+
flags.branch,
|
|
168
|
+
flags.commit,
|
|
169
|
+
);
|
|
157
170
|
|
|
158
171
|
const runtimeEnv = loadAndMergeEnv({
|
|
159
172
|
envFlags: flags.env,
|
|
@@ -197,7 +210,11 @@ interface AccountExecutionDefaults {
|
|
|
197
210
|
readonly cursorModel: string;
|
|
198
211
|
}
|
|
199
212
|
|
|
200
|
-
const NO_ACCOUNT_DEFAULTS: AccountExecutionDefaults = {
|
|
213
|
+
const NO_ACCOUNT_DEFAULTS: AccountExecutionDefaults = {
|
|
214
|
+
harness: "",
|
|
215
|
+
nativeModel: "",
|
|
216
|
+
cursorModel: "",
|
|
217
|
+
};
|
|
201
218
|
|
|
202
219
|
/**
|
|
203
220
|
* Best-effort read of the caller's execution defaults
|
|
@@ -206,12 +223,19 @@ const NO_ACCOUNT_DEFAULTS: AccountExecutionDefaults = { harness: "", nativeModel
|
|
|
206
223
|
* IdentityAccount — resolves to no defaults and the run proceeds exactly as
|
|
207
224
|
* unfilled flags do today: a missing preference must never fail a run.
|
|
208
225
|
*
|
|
226
|
+
* This reads `whoAmI` directly and never the SDK's ensureMyIdentityAccount:
|
|
227
|
+
* a run must not create an account as a side effect, so NOT_FOUND here means
|
|
228
|
+
* "no preferences yet". Provisioning belongs to `auth login` and `auth
|
|
229
|
+
* whoami`, the commands whose purpose is the caller's identity.
|
|
230
|
+
*
|
|
209
231
|
* The persisted harness value is allowlist-guarded to the shipped engines
|
|
210
232
|
* (the same guard the web launcher's useAccountExecutionDefaults applies):
|
|
211
233
|
* a client must not trust stored data it did not write, so anything outside
|
|
212
234
|
* native/cursor is treated as undeclared rather than stamped on the wire.
|
|
213
235
|
*/
|
|
214
|
-
async function resolveAccountExecutionDefaults(
|
|
236
|
+
async function resolveAccountExecutionDefaults(
|
|
237
|
+
client: Stigmer,
|
|
238
|
+
): Promise<AccountExecutionDefaults> {
|
|
215
239
|
try {
|
|
216
240
|
const account = await client.identityAccount.whoAmI();
|
|
217
241
|
const prefs = account.spec?.preferences;
|
|
@@ -257,7 +281,9 @@ export function parseApprovalAction(value: string): ApprovalAction {
|
|
|
257
281
|
*/
|
|
258
282
|
export function validateMode(mode: string): asserts mode is RunMode {
|
|
259
283
|
if (mode !== "" && mode !== "agent" && mode !== "plan") {
|
|
260
|
-
throw new UsageError(
|
|
284
|
+
throw new UsageError(
|
|
285
|
+
`invalid --mode value "${mode}": must be "agent" or "plan"`,
|
|
286
|
+
);
|
|
261
287
|
}
|
|
262
288
|
}
|
|
263
289
|
|
|
@@ -267,7 +293,9 @@ export function validateMode(mode: string): asserts mode is RunMode {
|
|
|
267
293
|
* server refuses fast on models without a registry fast variant; this check
|
|
268
294
|
* only catches spelling errors before a network round trip.
|
|
269
295
|
*/
|
|
270
|
-
export function validateServiceTier(
|
|
296
|
+
export function validateServiceTier(
|
|
297
|
+
tier: string,
|
|
298
|
+
): asserts tier is ServiceTierFlag {
|
|
271
299
|
if (tier !== "" && tier !== "standard" && tier !== "fast") {
|
|
272
300
|
throw new UsageError(
|
|
273
301
|
`invalid --service-tier value "${tier}": must be "standard" or "fast"`,
|
|
@@ -298,7 +326,9 @@ export function validateThinking(mode: string): asserts mode is ThinkingFlag {
|
|
|
298
326
|
* them here is honest, and this check catches spelling errors before a
|
|
299
327
|
* network round trip.
|
|
300
328
|
*/
|
|
301
|
-
export function validateHarness(
|
|
329
|
+
export function validateHarness(
|
|
330
|
+
harness: string,
|
|
331
|
+
): asserts harness is HarnessFlag {
|
|
302
332
|
if (harness !== "" && harness !== "native" && harness !== "cursor") {
|
|
303
333
|
throw new UsageError(
|
|
304
334
|
`invalid --harness value "${harness}": must be "native" or "cursor"`,
|