@desplega.ai/agent-swarm 1.95.0 → 1.97.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.
Files changed (36) hide show
  1. package/README.md +3 -3
  2. package/openapi.json +136 -1
  3. package/package.json +1 -1
  4. package/src/be/boot-scrub-logs.ts +76 -0
  5. package/src/be/db.ts +73 -10
  6. package/src/be/migrations/095_api_key_rate_limit_windows.sql +5 -0
  7. package/src/be/modelsdev-cache.json +89422 -85636
  8. package/src/be/scripts/boot-reembed.ts +57 -17
  9. package/src/be/scripts/embeddings.ts +26 -15
  10. package/src/commands/provider-credentials.ts +37 -15
  11. package/src/commands/runner.ts +68 -0
  12. package/src/http/agents.ts +1 -0
  13. package/src/http/api-keys.ts +51 -0
  14. package/src/http/config.ts +24 -4
  15. package/src/http/index.ts +9 -0
  16. package/src/prompts/session-templates.ts +21 -0
  17. package/src/providers/claude-adapter.ts +1 -0
  18. package/src/providers/codex-adapter.ts +3 -0
  19. package/src/providers/harness-version.ts +49 -2
  20. package/src/providers/pi-mono-adapter.ts +113 -19
  21. package/src/providers/types.ts +37 -9
  22. package/src/tests/api-key-tracking.test.ts +62 -0
  23. package/src/tests/bedrock-model-groups.test.ts +135 -0
  24. package/src/tests/credential-check.test.ts +361 -12
  25. package/src/tests/harness-version.test.ts +47 -0
  26. package/src/tests/opencode-adapter.test.ts +7 -6
  27. package/src/tests/providers/pi-cost.test.ts +7 -6
  28. package/src/tests/rate-limit-event.test.ts +37 -0
  29. package/src/tests/scripts-boot-reembed.test.ts +61 -2
  30. package/src/tests/scripts-embeddings.test.ts +27 -0
  31. package/src/tests/secret-scrubber.test.ts +73 -1
  32. package/src/tools/swarm-config/get-config.ts +9 -1
  33. package/src/tools/swarm-config/list-config.ts +8 -0
  34. package/src/types.ts +21 -0
  35. package/src/utils/error-tracker.ts +59 -0
  36. package/src/utils/secret-scrubber.ts +33 -12
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { describe, expect, mock, test } from "bun:test";
2
2
  import {
3
3
  buildCredStatusReport,
4
4
  checkProviderCredentials,
@@ -11,6 +11,7 @@ import { checkCodexCredentials } from "../providers/codex-adapter";
11
11
  import { checkDevinCredentials } from "../providers/devin-adapter";
12
12
  import { checkOpencodeCredentials } from "../providers/opencode-adapter";
13
13
  import { checkPiMonoCredentials } from "../providers/pi-mono-adapter";
14
+ import { AgentCredStatusSchema } from "../types";
14
15
 
15
16
  /** Build a stub `fs` whose `existsSync` returns true only for paths in the set. */
16
17
  function fsWith(present: Set<string>): { existsSync(p: string): boolean } {
@@ -138,7 +139,8 @@ describe("checkCodexCredentials", () => {
138
139
 
139
140
  /**
140
141
  * Stub probes for Bedrock tests. These replace the real @aws-sdk/client-bedrock
141
- * ListFoundationModels call so unit tests never hit AWS.
142
+ * `ListFoundationModels` + `ListInferenceProfiles` enumeration so unit tests
143
+ * never hit AWS.
142
144
  */
143
145
  const bedrockProbeSuccess = async () => {};
144
146
  const bedrockProbeAuthFail = async () => {
@@ -277,7 +279,10 @@ describe("checkPiMonoCredentials", () => {
277
279
  // "amazon-bedrock/", the probe runs. Tests inject a stub to avoid hitting AWS.
278
280
 
279
281
  test("amazon-bedrock: probe success → ready (sdk-delegated)", async () => {
280
- const env = { MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0" };
282
+ const env = {
283
+ MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
284
+ AWS_REGION: "us-east-1",
285
+ };
281
286
  const status = await checkPiMonoCredentials(env, {
282
287
  homeDir: HOME,
283
288
  fs: noFiles,
@@ -293,6 +298,7 @@ describe("checkPiMonoCredentials", () => {
293
298
  const env = {
294
299
  MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
295
300
  ANTHROPIC_API_KEY: "x",
301
+ AWS_REGION: "us-east-1",
296
302
  };
297
303
  const status = await checkPiMonoCredentials(env, {
298
304
  homeDir: HOME,
@@ -305,7 +311,10 @@ describe("checkPiMonoCredentials", () => {
305
311
 
306
312
  test("amazon-bedrock: probe success even when auth.json exists (Bedrock wins over file)", async () => {
307
313
  // auth.json holds Anthropic/OpenRouter/OpenAI creds — none used by Bedrock.
308
- const env = { MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0" };
314
+ const env = {
315
+ MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
316
+ AWS_REGION: "us-east-1",
317
+ };
309
318
  const status = await checkPiMonoCredentials(env, {
310
319
  homeDir: HOME,
311
320
  fs: fsWith(new Set([AUTH])),
@@ -316,7 +325,10 @@ describe("checkPiMonoCredentials", () => {
316
325
  });
317
326
 
318
327
  test("amazon-bedrock: provider-prefix match is case-insensitive", async () => {
319
- const env = { MODEL_OVERRIDE: "Amazon-Bedrock/anthropic.claude-sonnet-4-20250514-v1:0" };
328
+ const env = {
329
+ MODEL_OVERRIDE: "Amazon-Bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
330
+ AWS_REGION: "us-east-1",
331
+ };
320
332
  const status = await checkPiMonoCredentials(env, {
321
333
  homeDir: HOME,
322
334
  fs: noFiles,
@@ -327,7 +339,10 @@ describe("checkPiMonoCredentials", () => {
327
339
  });
328
340
 
329
341
  test("amazon-bedrock: probe auth failure → ready:false with aws-auth hint", async () => {
330
- const env = { MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0" };
342
+ const env = {
343
+ MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
344
+ AWS_REGION: "us-east-1",
345
+ };
331
346
  const status = await checkPiMonoCredentials(env, {
332
347
  homeDir: HOME,
333
348
  fs: noFiles,
@@ -338,7 +353,10 @@ describe("checkPiMonoCredentials", () => {
338
353
  });
339
354
 
340
355
  test("amazon-bedrock: probe access failure → ready:false with aws-access hint", async () => {
341
- const env = { MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0" };
356
+ const env = {
357
+ MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
358
+ AWS_REGION: "us-east-1",
359
+ };
342
360
  const status = await checkPiMonoCredentials(env, {
343
361
  homeDir: HOME,
344
362
  fs: noFiles,
@@ -349,7 +367,10 @@ describe("checkPiMonoCredentials", () => {
349
367
  });
350
368
 
351
369
  test("amazon-bedrock: probe region failure → ready:false (unclassified hint)", async () => {
352
- const env = { MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0" };
370
+ const env = {
371
+ MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
372
+ AWS_REGION: "us-east-1",
373
+ };
353
374
  const status = await checkPiMonoCredentials(env, {
354
375
  homeDir: HOME,
355
376
  fs: noFiles,
@@ -365,7 +386,11 @@ describe("checkPiMonoCredentials", () => {
365
386
  test("BEDROCK_AUTH_MODE=sdk: probe triggered even without amazon-bedrock/ prefix", async () => {
366
387
  // Explicit mode — MODEL_OVERRIDE can be anything (or absent); the Bedrock
367
388
  // path is taken because the operator explicitly declared BEDROCK_AUTH_MODE=sdk.
368
- const env = { BEDROCK_AUTH_MODE: "sdk", MODEL_OVERRIDE: "some-other-model" };
389
+ const env = {
390
+ BEDROCK_AUTH_MODE: "sdk",
391
+ MODEL_OVERRIDE: "some-other-model",
392
+ AWS_REGION: "us-east-1",
393
+ };
369
394
  const status = await checkPiMonoCredentials(env, {
370
395
  homeDir: HOME,
371
396
  fs: noFiles,
@@ -376,7 +401,7 @@ describe("checkPiMonoCredentials", () => {
376
401
  });
377
402
 
378
403
  test("BEDROCK_AUTH_MODE=sdk: probe failure → ready:false", async () => {
379
- const env = { BEDROCK_AUTH_MODE: "sdk" };
404
+ const env = { BEDROCK_AUTH_MODE: "sdk", AWS_REGION: "us-east-1" };
380
405
  const status = await checkPiMonoCredentials(env, {
381
406
  homeDir: HOME,
382
407
  fs: noFiles,
@@ -387,13 +412,13 @@ describe("checkPiMonoCredentials", () => {
387
412
 
388
413
  test("BEDROCK_AUTH_MODE=bearer: does NOT trigger the sdk probe (falls through)", async () => {
389
414
  // The bearer path is declared/validated but the full implementation is
390
- // out of scope for PR1. With no other credentials set it should be not-ready
415
+ // not implemented yet. With no other credentials set it should be not-ready
391
416
  // via the standard permissive check, not via the sdk probe.
392
417
  const env = { BEDROCK_AUTH_MODE: "bearer" };
393
418
  // No other keys set, no auth.json → not-ready from the permissive path.
394
419
  const status = await checkPiMonoCredentials(env, { homeDir: HOME, fs: noFiles });
395
420
  expect(status.ready).toBe(false);
396
- // Satisfying via any standard key still works for the bearer mode (PR1 scope).
421
+ // Satisfying via any standard key still works for the bearer mode today.
397
422
  const withKey = await checkPiMonoCredentials(
398
423
  { BEDROCK_AUTH_MODE: "bearer", ANTHROPIC_API_KEY: "x" },
399
424
  { homeDir: HOME, fs: noFiles },
@@ -410,6 +435,110 @@ describe("checkPiMonoCredentials", () => {
410
435
  expect(status.ready).toBe(true);
411
436
  expect(status.satisfiedBy).toBe("env");
412
437
  });
438
+
439
+ // ─── model enumeration (bedrockModels) ───────────────────────────────────
440
+
441
+ test("probe success with model list → bedrockModels populated", async () => {
442
+ const fakeModels = [
443
+ { id: "anthropic.claude-sonnet-4-20250514-v1:0", name: "Claude Sonnet 4" },
444
+ { id: "anthropic.claude-haiku-4-5-20251001-v1:0", name: "Claude Haiku 4.5" },
445
+ ];
446
+ const env = {
447
+ MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
448
+ AWS_REGION: "us-east-1",
449
+ };
450
+ const status = await checkPiMonoCredentials(env, {
451
+ homeDir: HOME,
452
+ fs: noFiles,
453
+ bedrockProbe: async () => fakeModels,
454
+ });
455
+ expect(status.ready).toBe(true);
456
+ expect(status.bedrockModels).toEqual(fakeModels);
457
+ expect(status.bedrockRegion).toBe("us-east-1");
458
+ });
459
+
460
+ test("probe success with void return → bedrockModels is empty array (backward compat)", async () => {
461
+ // Auth-only stubs return void — should not break enumeration callers.
462
+ const env = {
463
+ MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
464
+ AWS_REGION: "us-east-1",
465
+ };
466
+ const status = await checkPiMonoCredentials(env, {
467
+ homeDir: HOME,
468
+ fs: noFiles,
469
+ bedrockProbe: bedrockProbeSuccess, // returns void
470
+ });
471
+ expect(status.ready).toBe(true);
472
+ expect(status.bedrockModels).toEqual([]);
473
+ });
474
+
475
+ test("probe failure → bedrockModels is empty array and bedrockRegion is set", async () => {
476
+ const env = {
477
+ MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
478
+ AWS_REGION: "us-east-1",
479
+ };
480
+ const status = await checkPiMonoCredentials(env, {
481
+ homeDir: HOME,
482
+ fs: noFiles,
483
+ bedrockProbe: bedrockProbeAuthFail,
484
+ });
485
+ expect(status.ready).toBe(false);
486
+ expect(status.bedrockModels).toEqual([]);
487
+ expect(status.bedrockRegion).toBe("us-east-1");
488
+ });
489
+
490
+ test("probe uses AWS_REGION from env for bedrockRegion", async () => {
491
+ const env = {
492
+ MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
493
+ AWS_REGION: "eu-west-1",
494
+ };
495
+ const status = await checkPiMonoCredentials(env, {
496
+ homeDir: HOME,
497
+ fs: noFiles,
498
+ bedrockProbe: bedrockProbeSuccess,
499
+ });
500
+ expect(status.bedrockRegion).toBe("eu-west-1");
501
+ });
502
+
503
+ // ─── region not fabricated when AWS_REGION is unset ───────────────────────
504
+
505
+ test("AWS_REGION unset (sdk mode) → not-ready with set-region hint, no probe, no fabricated region", async () => {
506
+ // No us-east-1 fallback: enumerating a guessed region can differ from where
507
+ // inference runs. The probe must NOT run; report a not-ready Bedrock state.
508
+ let probeCalled = false;
509
+ const env = { BEDROCK_AUTH_MODE: "sdk" };
510
+ const status = await checkPiMonoCredentials(env, {
511
+ homeDir: HOME,
512
+ fs: noFiles,
513
+ bedrockProbe: async () => {
514
+ probeCalled = true;
515
+ return [];
516
+ },
517
+ });
518
+ expect(probeCalled).toBe(false);
519
+ expect(status.ready).toBe(false);
520
+ expect(status.hint).toContain("AWS_REGION");
521
+ expect(status.bedrockModels).toEqual([]);
522
+ // Empty string sentinel, NOT a fabricated "us-east-1".
523
+ expect(status.bedrockRegion).toBe("");
524
+ });
525
+
526
+ test("AWS_REGION unset (prefix inference) → not-ready, bedrock block still reported", async () => {
527
+ const env = { MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0" };
528
+ const status = await checkPiMonoCredentials(env, { homeDir: HOME, fs: noFiles });
529
+ expect(status.ready).toBe(false);
530
+ expect(status.bedrockRegion).toBe("");
531
+ expect(status.bedrockRegion).not.toBe("us-east-1");
532
+ });
533
+
534
+ test("non-Bedrock path → bedrockModels and bedrockRegion are undefined", async () => {
535
+ // Standard anthropic key path — no Bedrock probe runs.
536
+ const env = { ANTHROPIC_API_KEY: "x" };
537
+ const status = await checkPiMonoCredentials(env, { homeDir: HOME, fs: noFiles });
538
+ expect(status.ready).toBe(true);
539
+ expect(status.bedrockModels).toBeUndefined();
540
+ expect(status.bedrockRegion).toBeUndefined();
541
+ });
413
542
  });
414
543
 
415
544
  // ─── opencode ────────────────────────────────────────────────────────────────
@@ -590,4 +719,224 @@ describe("buildCredStatusReport", () => {
590
719
  const snap = await buildCredStatusReport("claude", {}, {}, "post_task");
591
720
  expect(snap.reportKind).toBe("post_task");
592
721
  });
722
+
723
+ // bedrock block in AgentCredStatus
724
+ test("Bedrock SDK mode: bedrock block included with live model list", async () => {
725
+ const fakeModels = [{ id: "anthropic.claude-sonnet-4-20250514-v1:0", name: "Claude Sonnet 4" }];
726
+ const env = {
727
+ MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
728
+ AWS_REGION: "us-east-1",
729
+ };
730
+ const snap = await buildCredStatusReport(
731
+ "pi",
732
+ env,
733
+ { bedrockProbe: async () => fakeModels },
734
+ "boot",
735
+ );
736
+ expect(snap.ready).toBe(true);
737
+ expect(snap.bedrock).not.toBeNull();
738
+ expect(snap.bedrock?.ready).toBe(true);
739
+ expect(snap.bedrock?.models).toEqual(fakeModels);
740
+ expect(snap.bedrock?.region).toBe("us-east-1");
741
+ expect(typeof snap.bedrock?.probedAt).toBe("number");
742
+ });
743
+
744
+ test("Bedrock SDK mode: probe failure → bedrock block has ready:false and empty models", async () => {
745
+ const env = {
746
+ MODEL_OVERRIDE: "amazon-bedrock/anthropic.claude-sonnet-4-20250514-v1:0",
747
+ AWS_REGION: "us-east-1",
748
+ };
749
+ const snap = await buildCredStatusReport(
750
+ "pi",
751
+ env,
752
+ { bedrockProbe: bedrockProbeAuthFail },
753
+ "boot",
754
+ );
755
+ expect(snap.ready).toBe(false);
756
+ expect(snap.bedrock).not.toBeNull();
757
+ expect(snap.bedrock?.ready).toBe(false);
758
+ expect(snap.bedrock?.models).toEqual([]);
759
+ expect(snap.bedrock?.error).toBeTruthy();
760
+ });
761
+
762
+ test("non-Bedrock pi mode → bedrock block is null", async () => {
763
+ const HOME = "/home/worker";
764
+ const snap = await buildCredStatusReport(
765
+ "pi",
766
+ { ANTHROPIC_API_KEY: "x" },
767
+ { homeDir: HOME, fs: noFiles },
768
+ "boot",
769
+ );
770
+ expect(snap.bedrock).toBeNull();
771
+ });
772
+
773
+ test("non-pi provider → bedrock block is null", async () => {
774
+ const snap = await buildCredStatusReport(
775
+ "claude",
776
+ { CLAUDE_CODE_OAUTH_TOKEN: "tok" },
777
+ {},
778
+ "boot",
779
+ );
780
+ expect(snap.bedrock).toBeNull();
781
+ });
782
+ });
783
+
784
+ // ─── schema round-trip ───────────────────────────────────────────────────────
785
+
786
+ describe("AgentCredStatusSchema round-trip with bedrock block", () => {
787
+ test("full bedrock block parses and serializes cleanly", () => {
788
+ const raw = {
789
+ ready: true,
790
+ missing: [],
791
+ satisfiedBy: "sdk-delegated",
792
+ hint: "AWS SDK credentials verified via ListFoundationModels (region: us-east-1).",
793
+ liveTest: null,
794
+ latestModel: null,
795
+ reportedAt: Date.now(),
796
+ reportKind: "boot",
797
+ bedrock: {
798
+ region: "us-east-1",
799
+ probedAt: Date.now(),
800
+ ready: true,
801
+ models: [
802
+ { id: "anthropic.claude-sonnet-4-20250514-v1:0", name: "Claude Sonnet 4" },
803
+ { id: "anthropic.claude-haiku-4-5-20251001-v1:0", name: "Claude Haiku 4.5" },
804
+ ],
805
+ },
806
+ };
807
+ const parsed = AgentCredStatusSchema.safeParse(raw);
808
+ expect(parsed.success).toBe(true);
809
+ if (parsed.success) {
810
+ expect(parsed.data.bedrock?.models).toHaveLength(2);
811
+ expect(parsed.data.bedrock?.ready).toBe(true);
812
+ expect(parsed.data.bedrock?.region).toBe("us-east-1");
813
+ }
814
+ });
815
+
816
+ test("bedrock block absent → defaults to null (backward compat)", () => {
817
+ const raw = {
818
+ ready: true,
819
+ missing: [],
820
+ satisfiedBy: "env",
821
+ hint: null,
822
+ liveTest: null,
823
+ latestModel: null,
824
+ reportedAt: Date.now(),
825
+ reportKind: "boot",
826
+ // No bedrock field
827
+ };
828
+ const parsed = AgentCredStatusSchema.safeParse(raw);
829
+ expect(parsed.success).toBe(true);
830
+ if (parsed.success) {
831
+ expect(parsed.data.bedrock).toBeNull();
832
+ }
833
+ });
834
+
835
+ test("bedrock block with error field parses correctly", () => {
836
+ const raw = {
837
+ ready: false,
838
+ missing: [],
839
+ satisfiedBy: null,
840
+ hint: "ExpiredToken",
841
+ liveTest: null,
842
+ latestModel: null,
843
+ reportedAt: Date.now(),
844
+ reportKind: "boot",
845
+ bedrock: {
846
+ region: "us-east-1",
847
+ probedAt: Date.now(),
848
+ ready: false,
849
+ models: [],
850
+ error: "Token expired — run aws sso login",
851
+ },
852
+ };
853
+ const parsed = AgentCredStatusSchema.safeParse(raw);
854
+ expect(parsed.success).toBe(true);
855
+ if (parsed.success) {
856
+ expect(parsed.data.bedrock?.ready).toBe(false);
857
+ expect(parsed.data.bedrock?.error).toBeDefined();
858
+ expect(parsed.data.bedrock?.models).toEqual([]);
859
+ }
860
+ });
861
+ });
862
+
863
+ // ─── usable set = harness-drivable ∩ (ON_DEMAND/ACTIVE FMs ∪ inference profiles) ─
864
+ // The real intersection lives in `runBedrockSdkProbeAndEnumerate`, which the
865
+ // injectable `bedrockProbe` stub bypasses. To exercise the union + filtering +
866
+ // intersection without real AWS credentials, stub `@aws-sdk/client-bedrock` and
867
+ // feed it canned list responses built from REAL pi-ai catalog ids.
868
+
869
+ describe("runBedrockSdkProbeAndEnumerate — intersection logic", () => {
870
+ test("includes inference-profile ids; drops non-ACTIVE and non-drivable ids", async () => {
871
+ const { getModels } = await import("@earendil-works/pi-ai");
872
+ const drivable = getModels("amazon-bedrock");
873
+ const isProfile = (id: string) => /^(us|eu|apac|au|global)\./.test(id);
874
+ const baseModel = drivable.find((m) => !isProfile(m.id));
875
+ const profileModel = drivable.find((m) => isProfile(m.id));
876
+ const legacyDrivable = drivable.find(
877
+ (m) => m.id !== baseModel?.id && m.id !== profileModel?.id,
878
+ );
879
+ if (!baseModel || !profileModel || !legacyDrivable) {
880
+ throw new Error("pi-ai amazon-bedrock catalog missing expected shapes");
881
+ }
882
+
883
+ // Capture which command kinds the client was asked to send.
884
+ const sentKinds: string[] = [];
885
+ mock.module("@aws-sdk/client-bedrock", () => ({
886
+ BedrockClient: class {
887
+ async send(cmd: { __kind: string }) {
888
+ sentKinds.push(cmd.__kind);
889
+ if (cmd.__kind === "fm") {
890
+ return {
891
+ modelSummaries: [
892
+ // ON_DEMAND/TEXT (request-filtered) + ACTIVE → kept.
893
+ { modelId: baseModel.id, modelLifecycle: { status: "ACTIVE" } },
894
+ // Drivable but NOT ACTIVE → dropped by lifecycle filter.
895
+ { modelId: legacyDrivable.id, modelLifecycle: { status: "LEGACY" } },
896
+ // ACTIVE but NOT in the pi-ai catalog → dropped by intersection.
897
+ { modelId: "amazon.not-a-real-pi-id-v9:0", modelLifecycle: { status: "ACTIVE" } },
898
+ ],
899
+ };
900
+ }
901
+ return {
902
+ inferenceProfileSummaries: [
903
+ // Cross-region profile id present in the pi-ai catalog → kept.
904
+ // This is exactly the class the old base-only intersection dropped.
905
+ { inferenceProfileId: profileModel.id },
906
+ // Profile id NOT in the pi-ai catalog → dropped by intersection.
907
+ { inferenceProfileId: "us.vendor.unknown-profile-v1:0" },
908
+ ],
909
+ };
910
+ }
911
+ },
912
+ ListFoundationModelsCommand: class {
913
+ __kind = "fm";
914
+ constructor(public input: unknown) {}
915
+ },
916
+ ListInferenceProfilesCommand: class {
917
+ __kind = "ip";
918
+ constructor(public input: unknown) {}
919
+ },
920
+ }));
921
+
922
+ const { runBedrockSdkProbeAndEnumerate } = await import("../providers/pi-mono-adapter");
923
+ const usable = await runBedrockSdkProbeAndEnumerate("us-east-1");
924
+ const ids = usable.map((m) => m.id);
925
+
926
+ // Both list calls were made (single bounded round-trip each).
927
+ expect(sentKinds).toContain("fm");
928
+ expect(sentKinds).toContain("ip");
929
+ // Base ACTIVE model kept.
930
+ expect(ids).toContain(baseModel.id);
931
+ // Inference-profile model kept — the regression this fix targets.
932
+ expect(ids).toContain(profileModel.id);
933
+ // Non-ACTIVE drivable dropped; non-catalog ids dropped.
934
+ expect(ids).not.toContain(legacyDrivable.id);
935
+ expect(ids).not.toContain("amazon.not-a-real-pi-id-v9:0");
936
+ expect(ids).not.toContain("us.vendor.unknown-profile-v1:0");
937
+ // Stored ids are pi-ai ids carrying the catalog name.
938
+ expect(usable.find((m) => m.id === profileModel.id)?.name).toBe(profileModel.name);
939
+
940
+ mock.restore();
941
+ });
593
942
  });
@@ -0,0 +1,47 @@
1
+ import { describe, expect, mock, test } from "bun:test";
2
+ import { readPkgVersion } from "../providers/harness-version";
3
+
4
+ describe("readPkgVersion", () => {
5
+ test("reads pi version from the CLI before package.json probes", () => {
6
+ const spawn = mock(() => ({ stdout: "0.79.1\n", stderr: "", status: 0 }));
7
+ const requirePackageJson = mock(() => ({ version: "0.0.0" }));
8
+
9
+ const version = readPkgVersion("@earendil-works/pi-coding-agent", {
10
+ requirePackageJson,
11
+ spawn,
12
+ });
13
+
14
+ expect(version).toBe("0.79.1");
15
+ expect(spawn).toHaveBeenCalledWith("pi", ["--version"], {
16
+ encoding: "utf8",
17
+ stdio: ["ignore", "pipe", "pipe"],
18
+ });
19
+ expect(requirePackageJson).not.toHaveBeenCalled();
20
+ });
21
+
22
+ test("reads opencode version from the CLI before package.json probes", () => {
23
+ const spawn = mock(() => ({ stdout: "opencode 1.16.2\n", stderr: "", status: 0 }));
24
+ const requirePackageJson = mock(() => ({ version: "0.0.0" }));
25
+
26
+ const version = readPkgVersion("@opencode-ai/sdk", {
27
+ requirePackageJson,
28
+ spawn,
29
+ });
30
+
31
+ expect(version).toBe("1.16.2");
32
+ expect(spawn).toHaveBeenCalledWith("opencode", ["--version"], {
33
+ encoding: "utf8",
34
+ stdio: ["ignore", "pipe", "pipe"],
35
+ });
36
+ expect(requirePackageJson).not.toHaveBeenCalled();
37
+ });
38
+
39
+ test("falls back to package.json when no CLI mapping returns a version", () => {
40
+ const version = readPkgVersion("@earendil-works/pi-coding-agent", {
41
+ requirePackageJson: () => ({ version: "0.79.1" }),
42
+ spawn: mock(() => ({ stdout: "", stderr: "", status: 0 })),
43
+ });
44
+
45
+ expect(version).toBe("0.79.1");
46
+ });
47
+ });
@@ -157,13 +157,14 @@ describe("OpencodeSession — SSE→ProviderEvent mapping", () => {
157
157
  const { emitted, result, serverCloseCalls } = await driveSession(events);
158
158
 
159
159
  const sessionInit = emitted.find((e) => e.type === "session_init");
160
- expect(sessionInit).toBeDefined();
161
- if (sessionInit?.type === "session_init") {
162
- expect(sessionInit.provider).toBe("opencode");
163
- expect(sessionInit.harnessVariant).toBe("stock");
164
- expect(typeof sessionInit.harnessVariantMeta?.version).toBe("string");
165
- expect((sessionInit.harnessVariantMeta?.version as string).length).toBeGreaterThan(0);
160
+ expect(sessionInit?.type).toBe("session_init");
161
+ if (sessionInit?.type !== "session_init") {
162
+ throw new Error("Expected opencode session_init event");
166
163
  }
164
+ expect(sessionInit.provider).toBe("opencode");
165
+ expect(sessionInit.harnessVariant).toBe("stock");
166
+ expect(typeof sessionInit.harnessVariantMeta?.version).toBe("string");
167
+ expect((sessionInit.harnessVariantMeta?.version as string).length).toBeGreaterThan(0);
167
168
 
168
169
  const resultEvent = emitted.find((e) => e.type === "result");
169
170
  expect(resultEvent).toBeDefined();
@@ -108,13 +108,14 @@ describe("PiMonoSession — provider tag on CostData", () => {
108
108
  session.onEvent((e) => events.push(e));
109
109
 
110
110
  const sessionInit = events.find((e) => e.type === "session_init");
111
- expect(sessionInit).toBeDefined();
112
- if (sessionInit?.type === "session_init") {
113
- expect(sessionInit.provider).toBe("pi");
114
- expect(sessionInit.harnessVariant).toBe("stock");
115
- expect(typeof sessionInit.harnessVariantMeta?.version).toBe("string");
116
- expect((sessionInit.harnessVariantMeta?.version as string).length).toBeGreaterThan(0);
111
+ expect(sessionInit?.type).toBe("session_init");
112
+ if (sessionInit?.type !== "session_init") {
113
+ throw new Error("Expected pi session_init event");
117
114
  }
115
+ expect(sessionInit.provider).toBe("pi");
116
+ expect(sessionInit.harnessVariant).toBe("stock");
117
+ expect(typeof sessionInit.harnessVariantMeta?.version).toBe("string");
118
+ expect((sessionInit.harnessVariantMeta?.version as string).length).toBeGreaterThan(0);
118
119
 
119
120
  const result = await session.waitForCompletion();
120
121
 
@@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test";
2
2
  import {
3
3
  isRateLimitMessage,
4
4
  MAX_RATE_LIMIT_RESET_MS,
5
+ parseRateLimitWindowTelemetry,
5
6
  parseStderrForErrors,
6
7
  SessionErrorTracker,
7
8
  trackErrorFromJson,
@@ -23,6 +24,42 @@ const FIXTURE_REJECTED = {
23
24
  };
24
25
 
25
26
  describe("SessionErrorTracker — rate_limit_event processing", () => {
27
+ test("captures allowed_warning telemetry without marking a cooldown", () => {
28
+ const tracker = new SessionErrorTracker();
29
+ tracker.processRateLimitEvent({
30
+ type: "rate_limit_event",
31
+ rate_limit_info: {
32
+ status: "allowed_warning",
33
+ resetsAt: 1781334000,
34
+ rateLimitType: "seven_day",
35
+ utilization: 0.82,
36
+ isUsingOverage: false,
37
+ surpassedThreshold: 0.75,
38
+ },
39
+ });
40
+
41
+ expect(tracker.getRateLimitResetAt()).toBeUndefined();
42
+ expect(tracker.getRateLimitWindows()).toEqual({
43
+ seven_day: expect.objectContaining({
44
+ status: "allowed_warning",
45
+ resetsAt: 1781334000,
46
+ utilization: 0.82,
47
+ isUsingOverage: false,
48
+ surpassedThreshold: 0.75,
49
+ }),
50
+ });
51
+ });
52
+
53
+ test("parseRateLimitWindowTelemetry is best-effort for malformed events", () => {
54
+ expect(parseRateLimitWindowTelemetry({ type: "rate_limit_event" })).toBeNull();
55
+ expect(
56
+ parseRateLimitWindowTelemetry({
57
+ type: "rate_limit_event",
58
+ rate_limit_info: { status: "allowed_warning", resetsAt: "bad" },
59
+ }),
60
+ ).toBeNull();
61
+ });
62
+
26
63
  test("stashes resetsAt (seconds) correctly as ms — verbatim CAI-1279 fixture", () => {
27
64
  const tracker = new SessionErrorTracker();
28
65
  tracker.processRateLimitEvent(FIXTURE_REJECTED);