stitchkit 0.59.3 → 0.60.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 (122) hide show
  1. package/README.md +14 -4
  2. package/dist/agent-runtime/admission-lanes.d.ts +35 -0
  3. package/dist/agent-runtime/admission-lanes.d.ts.map +1 -0
  4. package/dist/agent-runtime/coordinator.d.ts +36 -2
  5. package/dist/agent-runtime/coordinator.d.ts.map +1 -1
  6. package/dist/agent-runtime/events.d.ts +7 -0
  7. package/dist/agent-runtime/events.d.ts.map +1 -1
  8. package/dist/agent-runtime/history.d.ts +7 -0
  9. package/dist/agent-runtime/history.d.ts.map +1 -1
  10. package/dist/agent-runtime/models.d.ts +2 -3
  11. package/dist/agent-runtime/models.d.ts.map +1 -1
  12. package/dist/agent-runtime/observability.d.ts.map +1 -1
  13. package/dist/agent-runtime/run-execution.d.ts +31 -0
  14. package/dist/agent-runtime/run-execution.d.ts.map +1 -0
  15. package/dist/agent-runtime/runtime-internals.d.ts +25 -0
  16. package/dist/agent-runtime/runtime-internals.d.ts.map +1 -0
  17. package/dist/agent-runtime/runtime-result.d.ts +11 -0
  18. package/dist/agent-runtime/runtime-result.d.ts.map +1 -0
  19. package/dist/agent-runtime/runtime.d.ts +16 -15
  20. package/dist/agent-runtime/runtime.d.ts.map +1 -1
  21. package/dist/agent-runtime/schemas.d.ts.map +1 -1
  22. package/dist/agent-runtime/store-driver.d.ts.map +1 -1
  23. package/dist/agent-runtime/store.d.ts +9 -2
  24. package/dist/agent-runtime/store.d.ts.map +1 -1
  25. package/dist/agent-runtime/terminal-commit.d.ts.map +1 -1
  26. package/dist/agent-runtime/terminal-status.d.ts +13 -0
  27. package/dist/agent-runtime/terminal-status.d.ts.map +1 -0
  28. package/dist/agent-runtime.d.ts +5 -3
  29. package/dist/agent-runtime.d.ts.map +1 -1
  30. package/dist/agent-runtime.js +211 -156
  31. package/dist/application/activity.d.ts +7 -2
  32. package/dist/application/activity.d.ts.map +1 -1
  33. package/dist/application/grammy.d.ts +6 -0
  34. package/dist/application/grammy.d.ts.map +1 -1
  35. package/dist/application/health.d.ts +20 -1
  36. package/dist/application/health.d.ts.map +1 -1
  37. package/dist/application/kernel.d.ts +49 -3
  38. package/dist/application/kernel.d.ts.map +1 -1
  39. package/dist/application/opentelemetry.d.ts +25 -0
  40. package/dist/application/opentelemetry.d.ts.map +1 -0
  41. package/dist/application/schemas.d.ts +42 -0
  42. package/dist/application/schemas.d.ts.map +1 -1
  43. package/dist/application-opentelemetry.d.ts +2 -0
  44. package/dist/application-opentelemetry.d.ts.map +1 -0
  45. package/dist/application-opentelemetry.js +248 -0
  46. package/dist/application.d.ts +4 -4
  47. package/dist/application.d.ts.map +1 -1
  48. package/dist/application.js +69 -1243
  49. package/dist/cli.js +6 -6
  50. package/dist/contract/errors.d.ts +6 -0
  51. package/dist/contract/errors.d.ts.map +1 -1
  52. package/dist/contract/index.js +1 -1
  53. package/dist/declaration.d.ts +345 -0
  54. package/dist/declaration.d.ts.map +1 -0
  55. package/dist/declaration.js +183 -0
  56. package/dist/index-1zencgcb.js +651 -0
  57. package/dist/{index-sa2mbwa7.js → index-2akar0rq.js} +2 -2
  58. package/dist/{index-41wm56v0.js → index-3hn10n4c.js} +1 -1
  59. package/dist/{index-f6pymtqe.js → index-3w74r08v.js} +2 -2
  60. package/dist/{index-7c0gkyvj.js → index-6fbp58g8.js} +2 -2
  61. package/dist/{index-gnvzyw0k.js → index-8es1hrv4.js} +2 -2
  62. package/dist/index-8eywc9zv.js +100 -0
  63. package/dist/{index-3nwng7hs.js → index-8pc5s3pz.js} +4 -4
  64. package/dist/{index-28cqssm3.js → index-9h1vba8n.js} +6 -0
  65. package/dist/{index-0nc0cddp.js → index-a59da114.js} +17 -28
  66. package/dist/{index-0tszzfxp.js → index-f7521k0x.js} +25 -19
  67. package/dist/{index-y2rb7dwx.js → index-pzyt11ch.js} +5 -1
  68. package/dist/{index-mr617n62.js → index-svqhcrrj.js} +1 -1
  69. package/dist/{index-ps5pxp3h.js → index-v6e8hnkx.js} +7 -1
  70. package/dist/{index-1tkyng3g.js → index-vkk06pv1.js} +1 -9
  71. package/dist/index-x8v8b7rc.js +560 -0
  72. package/dist/index.js +1 -1
  73. package/dist/internal/observability-sink.d.ts.map +1 -1
  74. package/dist/internal/route-pattern.d.ts +9 -0
  75. package/dist/internal/route-pattern.d.ts.map +1 -1
  76. package/dist/node.d.ts +1 -1
  77. package/dist/node.d.ts.map +1 -1
  78. package/dist/node.js +12 -12
  79. package/dist/observability/index.js +5 -5
  80. package/dist/observability/sanitize.d.ts +3 -1
  81. package/dist/observability/sanitize.d.ts.map +1 -1
  82. package/dist/remote.js +3 -3
  83. package/dist/server/error-hook.d.ts +10 -3
  84. package/dist/server/error-hook.d.ts.map +1 -1
  85. package/dist/server/index.d.ts +1 -1
  86. package/dist/server/index.d.ts.map +1 -1
  87. package/dist/server/index.js +16 -19
  88. package/dist/server/middleware/cors.d.ts +5 -2
  89. package/dist/server/middleware/cors.d.ts.map +1 -1
  90. package/dist/server/openapi.d.ts.map +1 -1
  91. package/dist/server/router.d.ts.map +1 -1
  92. package/dist/server/socket-io-config.d.ts +68 -3
  93. package/dist/server/socket-io-config.d.ts.map +1 -1
  94. package/dist/server/socket-io-node.d.ts +1 -1
  95. package/dist/server/socket-io-node.d.ts.map +1 -1
  96. package/dist/server/socket-io.d.ts +1 -1
  97. package/dist/server/socket-io.d.ts.map +1 -1
  98. package/dist/testing/managed-resource-conformance-contract.d.ts +114 -0
  99. package/dist/testing/managed-resource-conformance-contract.d.ts.map +1 -0
  100. package/dist/testing/managed-resource-conformance-driver.d.ts +20 -0
  101. package/dist/testing/managed-resource-conformance-driver.d.ts.map +1 -0
  102. package/dist/testing/managed-resource-conformance-scenarios.d.ts +33 -0
  103. package/dist/testing/managed-resource-conformance-scenarios.d.ts.map +1 -0
  104. package/dist/testing/managed-resource-conformance.d.ts +5 -0
  105. package/dist/testing/managed-resource-conformance.d.ts.map +1 -0
  106. package/dist/testing/surface-manifest.d.ts.map +1 -1
  107. package/dist/testing.d.ts +1 -0
  108. package/dist/testing.d.ts.map +1 -1
  109. package/dist/testing.js +664 -78
  110. package/dist/tools/flatten-join.d.ts +2 -0
  111. package/dist/tools/flatten-join.d.ts.map +1 -1
  112. package/dist/tools/flatten.d.ts.map +1 -1
  113. package/dist/tools/mcp-prepare.d.ts +0 -2
  114. package/dist/tools/mcp-prepare.d.ts.map +1 -1
  115. package/dist/tools/runtime-tool.d.ts +0 -3
  116. package/dist/tools/runtime-tool.d.ts.map +1 -1
  117. package/dist/tools/schema.d.ts +0 -4
  118. package/dist/tools/schema.d.ts.map +1 -1
  119. package/dist/tools.js +9 -9
  120. package/llms-full.txt +1212 -67
  121. package/llms.txt +2 -0
  122. package/package.json +16 -3
package/dist/testing.js CHANGED
@@ -2,16 +2,21 @@ import {
2
2
  AgentMessageSchema,
3
3
  AgentRunSchema
4
4
  } from "./index-vtjgx3vv.js";
5
+ import {
6
+ createApplication
7
+ } from "./index-1zencgcb.js";
8
+ import"./index-8eywc9zv.js";
5
9
  import {
6
10
  RealtimeRequestDisconnectedError,
7
11
  RealtimeRequestInvalidAcknowledgementError,
8
12
  RealtimeRequestTimeoutError
9
13
  } from "./index-557by2db.js";
14
+ import"./index-dk6e56g0.js";
10
15
  import {
11
16
  mcpProjectionCandidate,
12
17
  prepareProjectedMcpTools,
13
18
  projectToolSurface
14
- } from "./index-1tkyng3g.js";
19
+ } from "./index-vkk06pv1.js";
15
20
  import"./index-6djpbnda.js";
16
21
  import {
17
22
  toJsonSchema
@@ -19,9 +24,11 @@ import {
19
24
  import {
20
25
  createClient,
21
26
  createClients
22
- } from "./index-mr617n62.js";
27
+ } from "./index-svqhcrrj.js";
23
28
  import"./index-48ffdxgk.js";
24
- import"./index-y2rb7dwx.js";
29
+ import {
30
+ joinRoutePath
31
+ } from "./index-pzyt11ch.js";
25
32
  import {
26
33
  isRecord
27
34
  } from "./index-smpbdg6k.js";
@@ -378,73 +385,649 @@ async function runAgentStoreConformance(config) {
378
385
  throw new Error("Abandon recovery did not atomically terminalize its assistant record");
379
386
  }
380
387
  }
388
+ // src/testing/managed-resource-conformance-contract.ts
389
+ import { z } from "zod";
390
+ var ManagedResourceConformanceScenarioIdSchema = z.enum([
391
+ "clean-shutdown",
392
+ "partial-start-rollback",
393
+ "readiness-rejection",
394
+ "completion-before-ready",
395
+ "required-late-completion",
396
+ "optional-late-completion",
397
+ "activation-rejection",
398
+ "shutdown-during-startup",
399
+ "force-after-stalled-close"
400
+ ]);
401
+ var ManagedResourceConformanceScenarioSchema = z.discriminatedUnion("id", [
402
+ z.object({ id: z.literal("clean-shutdown"), required: z.literal(true) }).readonly(),
403
+ z.object({ id: z.literal("partial-start-rollback"), required: z.literal(true) }).readonly(),
404
+ z.object({ id: z.literal("readiness-rejection"), required: z.literal(true) }).readonly(),
405
+ z.object({ id: z.literal("completion-before-ready"), required: z.literal(true) }).readonly(),
406
+ z.object({ id: z.literal("required-late-completion"), required: z.literal(true) }).readonly(),
407
+ z.object({ id: z.literal("optional-late-completion"), required: z.literal(false) }).readonly(),
408
+ z.object({ id: z.literal("activation-rejection"), required: z.literal(true) }).readonly(),
409
+ z.object({ id: z.literal("shutdown-during-startup"), required: z.literal(true) }).readonly(),
410
+ z.object({ id: z.literal("force-after-stalled-close"), required: z.literal(true) }).readonly()
411
+ ]);
412
+ var ManagedResourceConformancePhaseSchema = z.enum([
413
+ "start",
414
+ "readiness",
415
+ "completion",
416
+ "activate",
417
+ "stop-admission",
418
+ "drain",
419
+ "close",
420
+ "force",
421
+ "dispose"
422
+ ]);
423
+ var ManagedResourceConformanceTraceOutcomeSchema = z.enum([
424
+ "enter",
425
+ "resolve",
426
+ "reject"
427
+ ]);
428
+ var ManagedResourceConformanceTraceEntrySchema = z.object({
429
+ sequence: z.number().int().nonnegative(),
430
+ phase: ManagedResourceConformancePhaseSchema,
431
+ outcome: ManagedResourceConformanceTraceOutcomeSchema
432
+ }).strict().readonly();
433
+
434
+ class ManagedResourceConformanceError extends Error {
435
+ scenarioId;
436
+ expected;
437
+ trace;
438
+ code = "MANAGED_RESOURCE_CONFORMANCE_FAILED";
439
+ constructor(scenarioId, expected, trace, detail) {
440
+ const observed = trace.length === 0 ? "(empty)" : trace.map((entry) => `${entry.phase}:${entry.outcome}`).join(" -> ");
441
+ super(`[stitchkit] managed resource conformance "${scenarioId}" failed: ${detail}; ` + `expected ${expected.join(" -> ")}; observed ${observed}`);
442
+ this.scenarioId = scenarioId;
443
+ this.expected = expected;
444
+ this.trace = trace;
445
+ this.name = "ManagedResourceConformanceError";
446
+ }
447
+ }
448
+
449
+ // src/testing/managed-resource-conformance-driver.ts
450
+ function controlledPromise() {
451
+ const deferred = Promise.withResolvers();
452
+ let settled = false;
453
+ deferred.promise.catch(() => {
454
+ return;
455
+ });
456
+ return {
457
+ promise: deferred.promise,
458
+ resolve() {
459
+ if (settled)
460
+ return false;
461
+ settled = true;
462
+ deferred.resolve();
463
+ return true;
464
+ },
465
+ reject(error) {
466
+ if (settled)
467
+ return false;
468
+ settled = true;
469
+ deferred.reject(error);
470
+ return true;
471
+ }
472
+ };
473
+ }
474
+
475
+ class ManagedResourceConformanceTrace {
476
+ entries = [];
477
+ waiters = new Set;
478
+ record(phase, outcome) {
479
+ const entry = ManagedResourceConformanceTraceEntrySchema.parse({
480
+ sequence: this.entries.length,
481
+ phase,
482
+ outcome
483
+ });
484
+ this.entries.push(entry);
485
+ for (const waiter of this.waiters) {
486
+ if (waiter.phase !== phase || waiter.outcome !== outcome)
487
+ continue;
488
+ this.waiters.delete(waiter);
489
+ waiter.resolve();
490
+ }
491
+ }
492
+ has(phase, outcome) {
493
+ return this.entries.some((entry) => entry.phase === phase && entry.outcome === outcome);
494
+ }
495
+ count(phase, outcome) {
496
+ return this.entries.filter((entry) => entry.phase === phase && entry.outcome === outcome).length;
497
+ }
498
+ waitFor(phase, outcome = "enter") {
499
+ if (this.has(phase, outcome))
500
+ return Promise.resolve();
501
+ return new Promise((resolve) => this.waiters.add({ phase, outcome, resolve }));
502
+ }
503
+ }
504
+ function createManagedResourceConformanceGates(trace) {
505
+ const startup = controlledPromise();
506
+ const readiness = controlledPromise();
507
+ const completion = controlledPromise();
508
+ const activation = controlledPromise();
509
+ const close = controlledPromise();
510
+ const force = controlledPromise();
511
+ const gateFor = (phase) => {
512
+ if (phase === "start")
513
+ return startup;
514
+ if (phase === "readiness")
515
+ return readiness;
516
+ if (phase === "completion")
517
+ return completion;
518
+ if (phase === "activate")
519
+ return activation;
520
+ if (phase === "close")
521
+ return close;
522
+ return force;
523
+ };
524
+ const resolve = (phase) => {
525
+ if (gateFor(phase).resolve())
526
+ trace.record(phase, "resolve");
527
+ };
528
+ return {
529
+ controls: {
530
+ startup: startup.promise,
531
+ readiness: readiness.promise,
532
+ completion: completion.promise,
533
+ activation: activation.promise,
534
+ close: close.promise,
535
+ force: force.promise
536
+ },
537
+ resolve,
538
+ reject(phase, error) {
539
+ if (gateFor(phase).reject(error))
540
+ trace.record(phase, "reject");
541
+ },
542
+ releaseAll() {
543
+ resolve("start");
544
+ resolve("readiness");
545
+ resolve("completion");
546
+ resolve("activate");
547
+ resolve("close");
548
+ resolve("force");
549
+ }
550
+ };
551
+ }
552
+ async function observePhase(trace, phase, invoke) {
553
+ trace.record(phase, "enter");
554
+ try {
555
+ const value = await invoke();
556
+ trace.record(phase, "resolve");
557
+ return value;
558
+ } catch (error) {
559
+ trace.record(phase, "reject");
560
+ throw error;
561
+ }
562
+ }
563
+ function optionalPhase(resource, trace, phase, invoke) {
564
+ if (!invoke)
565
+ return {};
566
+ return {
567
+ [phase]: (context) => observePhase(trace, phase, () => invoke.call(resource, context))
568
+ };
569
+ }
570
+ function observeManagedResource(resource, required, trace) {
571
+ const start = async (context) => {
572
+ trace.record("start", "enter");
573
+ try {
574
+ const value = await resource.start(context);
575
+ trace.record("start", "resolve");
576
+ if (value === undefined)
577
+ return;
578
+ return value;
579
+ } catch (error) {
580
+ trace.record("start", "reject");
581
+ throw error;
582
+ }
583
+ };
584
+ return {
585
+ id: resource.id,
586
+ required,
587
+ start,
588
+ ...optionalPhase(resource, trace, "activate", resource.activate),
589
+ ...resource.stopAdmission ? {
590
+ stopAdmission: (context) => observePhase(trace, "stop-admission", () => resource.stopAdmission?.call(resource, context))
591
+ } : {},
592
+ ...optionalPhase(resource, trace, "drain", resource.drain),
593
+ ...optionalPhase(resource, trace, "close", resource.close),
594
+ ...optionalPhase(resource, trace, "force", resource.force)
595
+ };
596
+ }
597
+
598
+ // src/testing/managed-resource-conformance-scenarios.ts
599
+ var MANAGED_RESOURCE_CONFORMANCE_SCENARIOS = ManagedResourceConformanceScenarioSchema.array().parse([
600
+ { id: "clean-shutdown", required: true },
601
+ { id: "partial-start-rollback", required: true },
602
+ { id: "readiness-rejection", required: true },
603
+ { id: "completion-before-ready", required: true },
604
+ { id: "required-late-completion", required: true },
605
+ { id: "optional-late-completion", required: false },
606
+ { id: "activation-rejection", required: true },
607
+ { id: "shutdown-during-startup", required: true },
608
+ { id: "force-after-stalled-close", required: true }
609
+ ]);
610
+ var EXPECTED_TRACE = {
611
+ "clean-shutdown": [
612
+ "start:enter",
613
+ "start:resolve",
614
+ "readiness:resolve",
615
+ "activate:enter",
616
+ "activate:resolve",
617
+ "stop-admission:enter",
618
+ "drain:enter",
619
+ "close:enter",
620
+ "close:resolve"
621
+ ],
622
+ "partial-start-rollback": ["start:enter", "start:reject", "close:enter", "close:resolve"],
623
+ "readiness-rejection": [
624
+ "start:enter",
625
+ "start:resolve",
626
+ "readiness:reject",
627
+ "close:enter",
628
+ "close:resolve"
629
+ ],
630
+ "completion-before-ready": [
631
+ "start:enter",
632
+ "start:resolve",
633
+ "completion:reject",
634
+ "close:enter",
635
+ "close:resolve"
636
+ ],
637
+ "required-late-completion": [
638
+ "activate:resolve",
639
+ "completion:reject",
640
+ "stop-admission:enter",
641
+ "drain:enter",
642
+ "close:enter"
643
+ ],
644
+ "optional-late-completion": [
645
+ "activate:resolve",
646
+ "completion:reject",
647
+ "stop-admission:enter",
648
+ "drain:enter",
649
+ "close:enter"
650
+ ],
651
+ "activation-rejection": [
652
+ "readiness:resolve",
653
+ "activate:enter",
654
+ "activate:reject",
655
+ "close:enter",
656
+ "close:resolve"
657
+ ],
658
+ "shutdown-during-startup": ["start:enter", "start:resolve", "close:enter", "close:resolve"],
659
+ "force-after-stalled-close": [
660
+ "stop-admission:enter",
661
+ "drain:enter",
662
+ "close:enter",
663
+ "force:enter",
664
+ "force:resolve"
665
+ ]
666
+ };
667
+ function outcomeOf(promise) {
668
+ return promise.then(() => ({ status: "resolved" }), (error) => ({ status: "rejected", error }));
669
+ }
670
+ function requireCondition(condition, detail) {
671
+ if (!condition)
672
+ throw new Error(detail);
673
+ }
674
+ function observedTrace(trace) {
675
+ return trace.entries.map((entry) => `${entry.phase}:${entry.outcome}`);
676
+ }
677
+ function requireTrace(runtime) {
678
+ const expected = EXPECTED_TRACE[runtime.scenario.id];
679
+ const observed = observedTrace(runtime.trace);
680
+ let cursor = 0;
681
+ for (const entry of observed) {
682
+ if (entry === expected[cursor])
683
+ cursor += 1;
684
+ }
685
+ requireCondition(cursor === expected.length, `phase trace omitted or reordered "${expected[cursor] ?? "unknown"}"`);
686
+ }
687
+ async function phaseOrSettlement(trace, phase, settlement) {
688
+ return Promise.race([
689
+ trace.waitFor(phase).then(() => "phase"),
690
+ settlement.then(() => "settled", () => "settled")
691
+ ]);
692
+ }
693
+ async function startSuccessfully(runtime) {
694
+ const starting = runtime.application.start();
695
+ const startOutcome = outcomeOf(starting);
696
+ await runtime.trace.waitFor("start");
697
+ runtime.gates.resolve("start");
698
+ runtime.gates.resolve("readiness");
699
+ const activation = await Promise.race([
700
+ runtime.trace.waitFor("activate").then(() => ({ status: "resolved" })),
701
+ startOutcome
702
+ ]);
703
+ if (activation.status === "rejected")
704
+ throw activation.error;
705
+ runtime.gates.resolve("activate");
706
+ const outcome = await startOutcome;
707
+ requireCondition(outcome.status === "resolved", "application did not complete successful startup");
708
+ }
709
+ async function closeNormally(runtime) {
710
+ const shuttingDown = runtime.application.shutdown();
711
+ const close = await phaseOrSettlement(runtime.trace, "close", shuttingDown);
712
+ if (close === "phase")
713
+ runtime.gates.resolve("close");
714
+ const outcome = await outcomeOf(shuttingDown);
715
+ requireCondition(outcome.status === "resolved", "application shutdown rejected");
716
+ }
717
+ async function expectStartupFailure(runtime, starting) {
718
+ const close = await phaseOrSettlement(runtime.trace, "close", starting);
719
+ if (close === "phase")
720
+ runtime.gates.resolve("close");
721
+ const outcome = await outcomeOf(starting);
722
+ if (outcome.status === "resolved") {
723
+ await closeNormally(runtime);
724
+ throw new Error("application startup unexpectedly resolved");
725
+ }
726
+ requireCondition(runtime.trace.count("close", "enter") === 1, "attempted resource was not closed once");
727
+ await runtime.application.shutdown();
728
+ }
729
+ function waitForSnapshot(application, predicate) {
730
+ const deferred = Promise.withResolvers();
731
+ let unsubscribe = () => {
732
+ return;
733
+ };
734
+ unsubscribe = application.subscribe((snapshot) => {
735
+ if (!predicate(snapshot))
736
+ return;
737
+ unsubscribe();
738
+ deferred.resolve(snapshot);
739
+ });
740
+ return deferred.promise;
741
+ }
742
+ async function cleanShutdown(runtime) {
743
+ await startSuccessfully(runtime);
744
+ await closeNormally(runtime);
745
+ requireCondition(runtime.application.getSnapshot().lifecycle === "stopped", "clean shutdown did not stop the application");
746
+ }
747
+ async function partialStartRollback(runtime) {
748
+ const starting = runtime.application.start();
749
+ await runtime.trace.waitFor("start");
750
+ runtime.gates.reject("start", new Error("controlled partial startup rejection"));
751
+ await expectStartupFailure(runtime, starting);
752
+ }
753
+ async function readinessRejection(runtime) {
754
+ const starting = runtime.application.start();
755
+ await runtime.trace.waitFor("start");
756
+ runtime.gates.resolve("start");
757
+ await runtime.trace.waitFor("start", "resolve");
758
+ runtime.gates.reject("readiness", new Error("controlled readiness rejection"));
759
+ runtime.gates.resolve("activate");
760
+ await expectStartupFailure(runtime, starting);
761
+ }
762
+ async function completionBeforeReady(runtime) {
763
+ const starting = runtime.application.start();
764
+ await runtime.trace.waitFor("start");
765
+ runtime.gates.resolve("start");
766
+ await runtime.trace.waitFor("start", "resolve");
767
+ runtime.gates.reject("completion", new Error("controlled early completion rejection"));
768
+ runtime.gates.resolve("readiness");
769
+ runtime.gates.resolve("activate");
770
+ await expectStartupFailure(runtime, starting);
771
+ }
772
+ async function lateCompletion(runtime) {
773
+ await startSuccessfully(runtime);
774
+ const expectedHealth = runtime.scenario.required ? "unhealthy" : "degraded";
775
+ const changed = waitForSnapshot(runtime.application, (snapshot2) => snapshot2.health === expectedHealth);
776
+ runtime.gates.reject("completion", new Error("controlled late completion rejection"));
777
+ const snapshot = await changed;
778
+ requireCondition(snapshot.lifecycle === "ready", "late completion changed lifecycle ownership");
779
+ requireCondition(snapshot.health === expectedHealth, "late completion projected the wrong health");
780
+ requireCondition(snapshot.ready === !runtime.scenario.required, "late completion projected the wrong readiness");
781
+ requireCondition(snapshot.admission.accepting === !runtime.scenario.required, "late completion projected the wrong admission state");
782
+ await closeNormally(runtime);
783
+ }
784
+ async function activationRejection(runtime) {
785
+ const starting = runtime.application.start();
786
+ await runtime.trace.waitFor("start");
787
+ runtime.gates.resolve("start");
788
+ await runtime.trace.waitFor("start", "resolve");
789
+ runtime.gates.resolve("readiness");
790
+ await runtime.trace.waitFor("activate");
791
+ runtime.gates.reject("activate", new Error("controlled activation rejection"));
792
+ await expectStartupFailure(runtime, starting);
793
+ }
794
+ async function shutdownDuringStartup(runtime) {
795
+ const starting = runtime.application.start();
796
+ await runtime.trace.waitFor("start");
797
+ const shuttingDown = runtime.application.shutdown();
798
+ runtime.gates.resolve("start");
799
+ const close = await phaseOrSettlement(runtime.trace, "close", shuttingDown);
800
+ if (close === "phase")
801
+ runtime.gates.resolve("close");
802
+ const [startOutcome, shutdownOutcome] = await Promise.all([
803
+ outcomeOf(starting),
804
+ outcomeOf(shuttingDown)
805
+ ]);
806
+ requireCondition(startOutcome.status === "rejected", "startup won the shutdown race");
807
+ requireCondition(shutdownOutcome.status === "resolved", "shutdown race rejected");
808
+ requireCondition(!runtime.trace.has("activate", "enter"), "resource activated after shutdown won");
809
+ }
810
+ async function forceAfterStalledClose(runtime) {
811
+ await startSuccessfully(runtime);
812
+ const controller = new AbortController;
813
+ const shuttingDown = runtime.application.shutdown({
814
+ gracePeriodMs: 60000,
815
+ forceTimeoutMs: 60000,
816
+ signal: controller.signal
817
+ });
818
+ await runtime.trace.waitFor("close");
819
+ controller.abort();
820
+ const force = await phaseOrSettlement(runtime.trace, "force", shuttingDown);
821
+ if (force === "phase")
822
+ runtime.gates.resolve("force");
823
+ const result = await shuttingDown;
824
+ requireCondition(runtime.trace.count("close", "enter") === 1, "close was not invoked exactly once");
825
+ requireCondition(runtime.trace.count("force", "enter") === 1, "unfinished close did not invoke force");
826
+ requireCondition(result.outcome === "forced", "forced shutdown reported a clean outcome");
827
+ requireCondition(result.reason === "signal", "forced shutdown lost its signal reason");
828
+ runtime.gates.resolve("close");
829
+ }
830
+ function expectedManagedResourceConformanceTrace(scenarioId) {
831
+ return EXPECTED_TRACE[scenarioId];
832
+ }
833
+ async function executeManagedResourceConformanceScenario(scenario, resource, gates, trace) {
834
+ const application = createApplication({
835
+ id: `conformance-${scenario.id}`,
836
+ resources: [observeManagedResource(resource, scenario.required, trace)]
837
+ });
838
+ const runtime = { scenario, application, gates, trace };
839
+ if (scenario.id === "clean-shutdown")
840
+ await cleanShutdown(runtime);
841
+ else if (scenario.id === "partial-start-rollback")
842
+ await partialStartRollback(runtime);
843
+ else if (scenario.id === "readiness-rejection")
844
+ await readinessRejection(runtime);
845
+ else if (scenario.id === "completion-before-ready")
846
+ await completionBeforeReady(runtime);
847
+ else if (scenario.id === "required-late-completion" || scenario.id === "optional-late-completion") {
848
+ await lateCompletion(runtime);
849
+ } else if (scenario.id === "activation-rejection")
850
+ await activationRejection(runtime);
851
+ else if (scenario.id === "shutdown-during-startup")
852
+ await shutdownDuringStartup(runtime);
853
+ else
854
+ await forceAfterStalledClose(runtime);
855
+ requireTrace(runtime);
856
+ }
857
+
858
+ // src/testing/managed-resource-conformance.ts
859
+ var DEFAULT_WATCHDOG_TIMEOUT_MS = 2000;
860
+
861
+ class ManagedResourceConformanceWatchdogError extends Error {
862
+ phase;
863
+ constructor(phase) {
864
+ super(`emergency watchdog expired during ${phase}`);
865
+ this.phase = phase;
866
+ this.name = "ManagedResourceConformanceWatchdogError";
867
+ }
868
+ }
869
+ async function withWatchdog(promise, timeoutMs, phase) {
870
+ let timer;
871
+ const watchdog = new Promise((_resolve, reject) => {
872
+ timer = setTimeout(() => reject(new ManagedResourceConformanceWatchdogError(phase)), timeoutMs);
873
+ });
874
+ try {
875
+ return await Promise.race([promise, watchdog]);
876
+ } finally {
877
+ if (timer !== undefined)
878
+ clearTimeout(timer);
879
+ }
880
+ }
881
+ function detailOf(error) {
882
+ if (error instanceof ManagedResourceConformanceWatchdogError)
883
+ return error.message;
884
+ if (error instanceof Error)
885
+ return error.message;
886
+ return "unknown conformance failure";
887
+ }
888
+ async function runScenario(config, scenario, watchdogTimeoutMs) {
889
+ const trace = new ManagedResourceConformanceTrace;
890
+ const gates = createManagedResourceConformanceGates(trace);
891
+ const fixture = await withWatchdog(Promise.resolve().then(() => config.createFixture({ scenario, controls: gates.controls })), watchdogTimeoutMs, "factory").catch((error) => {
892
+ throw new ManagedResourceConformanceError(scenario.id, expectedManagedResourceConformanceTrace(scenario.id), trace.entries, detailOf(error));
893
+ });
894
+ let failure;
895
+ const execution = executeManagedResourceConformanceScenario(scenario, fixture.resource, gates, trace);
896
+ execution.catch(() => {
897
+ return;
898
+ });
899
+ let settlementIncomplete = false;
900
+ try {
901
+ await withWatchdog(execution, watchdogTimeoutMs, "scenario");
902
+ } catch (error) {
903
+ failure = { value: error };
904
+ } finally {
905
+ gates.releaseAll();
906
+ if (failure?.value instanceof ManagedResourceConformanceWatchdogError) {
907
+ try {
908
+ await withWatchdog(execution, watchdogTimeoutMs, "settlement");
909
+ } catch (error) {
910
+ if (error instanceof ManagedResourceConformanceWatchdogError) {
911
+ settlementIncomplete = true;
912
+ } else {
913
+ failure = { value: error };
914
+ }
915
+ }
916
+ }
917
+ trace.record("dispose", "enter");
918
+ try {
919
+ await withWatchdog(Promise.resolve(fixture.dispose()), watchdogTimeoutMs, "dispose");
920
+ trace.record("dispose", "resolve");
921
+ } catch (error) {
922
+ trace.record("dispose", "reject");
923
+ if (failure === undefined)
924
+ failure = { value: error };
925
+ }
926
+ if (settlementIncomplete) {
927
+ try {
928
+ await withWatchdog(execution, watchdogTimeoutMs, "settlement");
929
+ settlementIncomplete = false;
930
+ } catch (error) {
931
+ if (error instanceof ManagedResourceConformanceWatchdogError) {
932
+ const original = failure?.value;
933
+ failure = {
934
+ value: new Error(`${detailOf(original)}; execution remained live after fixture disposal`)
935
+ };
936
+ } else {
937
+ settlementIncomplete = false;
938
+ failure = { value: error };
939
+ }
940
+ }
941
+ }
942
+ }
943
+ if (failure !== undefined) {
944
+ if (failure.value instanceof ManagedResourceConformanceError)
945
+ throw failure.value;
946
+ throw new ManagedResourceConformanceError(scenario.id, expectedManagedResourceConformanceTrace(scenario.id), trace.entries, detailOf(failure.value));
947
+ }
948
+ }
949
+ async function runManagedResourceConformance(config) {
950
+ const watchdogTimeoutMs = config.watchdogTimeoutMs ?? DEFAULT_WATCHDOG_TIMEOUT_MS;
951
+ if (!Number.isSafeInteger(watchdogTimeoutMs) || watchdogTimeoutMs <= 0) {
952
+ throw new TypeError("[stitchkit] managed resource conformance watchdogTimeoutMs must be positive");
953
+ }
954
+ const selected = new Set((config.scenarios ?? MANAGED_RESOURCE_CONFORMANCE_SCENARIOS.map((scenario) => scenario.id)).map((scenario) => ManagedResourceConformanceScenarioIdSchema.parse(scenario)));
955
+ if (selected.size === 0) {
956
+ throw new TypeError("[stitchkit] managed resource conformance scenarios must not be empty");
957
+ }
958
+ for (const scenario of MANAGED_RESOURCE_CONFORMANCE_SCENARIOS) {
959
+ if (!selected.has(scenario.id))
960
+ continue;
961
+ await runScenario(config, scenario, watchdogTimeoutMs);
962
+ }
963
+ }
381
964
  // src/testing/surface-conformance.ts
382
- import { z as z2 } from "zod";
965
+ import { z as z3 } from "zod";
383
966
 
384
967
  // src/testing/surface-manifest.ts
385
968
  import { createHash } from "node:crypto";
386
- import { z } from "zod";
387
- var HttpMethodSchema = z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]);
388
- var ToolTransportSchema = z.enum(["MCP", "AGENT", "CLI"]);
389
- var SurfaceTransportSchema = z.enum(["HTTP", "MCP", "AGENT", "CLI", "REALTIME"]);
390
- var SurfaceSchemaDigestsSchema = z.object({
391
- params: z.string().nullable(),
392
- input: z.string().nullable(),
393
- output: z.string().nullable(),
394
- multipart: z.string().nullable()
969
+ import { z as z2 } from "zod";
970
+ var HttpMethodSchema = z2.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]);
971
+ var ToolTransportSchema = z2.enum(["MCP", "AGENT", "CLI"]);
972
+ var SurfaceTransportSchema = z2.enum(["HTTP", "MCP", "AGENT", "CLI", "REALTIME"]);
973
+ var SurfaceSchemaDigestsSchema = z2.object({
974
+ params: z2.string().nullable(),
975
+ input: z2.string().nullable(),
976
+ output: z2.string().nullable(),
977
+ multipart: z2.string().nullable()
395
978
  });
396
- var SurfaceManifestOperationSchema = z.object({
397
- kind: z.enum(["contract", "runtime"]),
398
- service: z.string(),
399
- action: z.string(),
979
+ var SurfaceManifestOperationSchema = z2.object({
980
+ kind: z2.enum(["contract", "runtime"]),
981
+ service: z2.string(),
982
+ action: z2.string(),
400
983
  method: HttpMethodSchema,
401
- scope: z.string().nullable(),
402
- description: z.string(),
984
+ scope: z2.string().nullable(),
985
+ description: z2.string(),
403
986
  schemas: SurfaceSchemaDigestsSchema,
404
- http: z.array(z.object({ method: HttpMethodSchema, path: z.string() }))
987
+ http: z2.array(z2.object({ method: HttpMethodSchema, path: z2.string() }))
405
988
  });
406
- var SurfaceManifestToolSchema = z.object({
407
- kind: z.enum(["contract", "runtime"]),
408
- service: z.string(),
409
- action: z.string(),
410
- name: z.string(),
411
- input: z.string()
989
+ var SurfaceManifestToolSchema = z2.object({
990
+ kind: z2.enum(["contract", "runtime"]),
991
+ service: z2.string(),
992
+ action: z2.string(),
993
+ name: z2.string(),
994
+ input: z2.string()
412
995
  });
413
- var SurfaceManifestToolSurfaceSchema = z.object({
996
+ var SurfaceManifestToolSurfaceSchema = z2.object({
414
997
  transport: ToolTransportSchema,
415
- surface: z.string().nullable(),
416
- tools: z.array(SurfaceManifestToolSchema)
998
+ surface: z2.string().nullable(),
999
+ tools: z2.array(SurfaceManifestToolSchema)
417
1000
  });
418
- var SurfaceManifestCliCommandSchema = z.object({
419
- name: z.string(),
420
- description: z.string(),
421
- input: z.string(),
422
- output: z.string().nullable()
1001
+ var SurfaceManifestCliCommandSchema = z2.object({
1002
+ name: z2.string(),
1003
+ description: z2.string(),
1004
+ input: z2.string(),
1005
+ output: z2.string().nullable()
423
1006
  });
424
- var SurfaceManifestExtensionSchema = z.object({
425
- name: z.string(),
1007
+ var SurfaceManifestExtensionSchema = z2.object({
1008
+ name: z2.string(),
426
1009
  transport: SurfaceTransportSchema
427
1010
  });
428
- var SurfaceRealtimeSchemaPairSchema = z.object({
429
- input: z.string(),
430
- output: z.string()
1011
+ var SurfaceRealtimeSchemaPairSchema = z2.object({
1012
+ input: z2.string(),
1013
+ output: z2.string()
431
1014
  });
432
- var SurfaceManifestRealtimeEventSchema = z.object({
433
- contract: z.string(),
434
- direction: z.enum(["serverToClient", "clientToServer"]),
435
- event: z.string(),
1015
+ var SurfaceManifestRealtimeEventSchema = z2.object({
1016
+ contract: z2.string(),
1017
+ direction: z2.enum(["serverToClient", "clientToServer"]),
1018
+ event: z2.string(),
436
1019
  args: SurfaceRealtimeSchemaPairSchema,
437
1020
  acknowledgement: SurfaceRealtimeSchemaPairSchema.nullable()
438
1021
  });
439
- var SurfaceManifestSchema = z.object({
440
- manifestVersion: z.literal(2),
441
- digestVersion: z.literal(1),
442
- operations: z.array(SurfaceManifestOperationSchema),
443
- toolSurfaces: z.array(SurfaceManifestToolSurfaceSchema),
444
- realtimeContracts: z.array(z.string()),
445
- realtime: z.array(SurfaceManifestRealtimeEventSchema),
446
- cliOnly: z.array(SurfaceManifestCliCommandSchema),
447
- extensions: z.array(SurfaceManifestExtensionSchema)
1022
+ var SurfaceManifestSchema = z2.object({
1023
+ manifestVersion: z2.literal(2),
1024
+ digestVersion: z2.literal(1),
1025
+ operations: z2.array(SurfaceManifestOperationSchema),
1026
+ toolSurfaces: z2.array(SurfaceManifestToolSurfaceSchema),
1027
+ realtimeContracts: z2.array(z2.string()),
1028
+ realtime: z2.array(SurfaceManifestRealtimeEventSchema),
1029
+ cliOnly: z2.array(SurfaceManifestCliCommandSchema),
1030
+ extensions: z2.array(SurfaceManifestExtensionSchema)
448
1031
  });
449
1032
  function compareCodepoints(left, right) {
450
1033
  if (left < right)
@@ -494,10 +1077,6 @@ function presentationDigest(schema) {
494
1077
  function multipartDigest(value) {
495
1078
  return value === undefined ? null : digestValue(value);
496
1079
  }
497
- function joinPath(...parts) {
498
- const joined = parts.filter((part) => Boolean(part)).map((part) => part.replace(/^\/+|\/+$/g, "")).filter(Boolean).join("/");
499
- return `/${joined}`;
500
- }
501
1080
  function operationKey(kind, service, action) {
502
1081
  return `${kind}\x00${service}\x00${action}`;
503
1082
  }
@@ -583,7 +1162,7 @@ function buildSurfaceManifest(config) {
583
1162
  continue;
584
1163
  const route = {
585
1164
  method: method.method,
586
- path: joinPath(prefix, service.prefix, method.path)
1165
+ path: joinRoutePath(prefix, service.prefix, method.path)
587
1166
  };
588
1167
  if (!operation.http.some((entry) => entry.method === route.method && entry.path === route.path)) {
589
1168
  operation.http.push(route);
@@ -729,47 +1308,47 @@ actual: ${actual}`);
729
1308
  }
730
1309
 
731
1310
  // src/testing/surface-conformance.ts
732
- var RealtimeRejectionObservationSchema = z2.object({
733
- direction: z2.enum([
1311
+ var RealtimeRejectionObservationSchema = z3.object({
1312
+ direction: z3.enum([
734
1313
  "client-inbound",
735
1314
  "client-outbound",
736
1315
  "server-inbound",
737
1316
  "server-outbound"
738
1317
  ]),
739
- phase: z2.enum(["arguments", "acknowledgement"]),
740
- reason: z2.enum([
1318
+ phase: z3.enum(["arguments", "acknowledgement"]),
1319
+ reason: z3.enum([
741
1320
  "unknown-event",
742
1321
  "invalid-arguments",
743
1322
  "invalid-acknowledgement-value",
744
1323
  "missing-acknowledgement"
745
1324
  ]),
746
- fault: z2.enum(["peer", "local"])
1325
+ fault: z3.enum(["peer", "local"])
747
1326
  });
748
1327
  var ObservationFields = {
749
- code: z2.string().optional(),
750
- data: z2.unknown().optional(),
751
- diagnostic: z2.string().optional(),
752
- handlerCalls: z2.number().int().nonnegative().optional()
1328
+ code: z3.string().optional(),
1329
+ data: z3.unknown().optional(),
1330
+ diagnostic: z3.string().optional(),
1331
+ handlerCalls: z3.number().int().nonnegative().optional()
753
1332
  };
754
- var RealtimeDisconnectObservationSchema = z2.object({
755
- phase: z2.enum(["before-invoke", "in-flight"])
1333
+ var RealtimeDisconnectObservationSchema = z3.object({
1334
+ phase: z3.enum(["before-invoke", "in-flight"])
756
1335
  });
757
- var TransportObservationSchema = z2.discriminatedUnion("outcome", [
758
- z2.object({ outcome: z2.literal("success"), ...ObservationFields }),
759
- z2.object({ outcome: z2.literal("validation_error"), ...ObservationFields }),
760
- z2.object({ outcome: z2.literal("domain_error"), ...ObservationFields }),
761
- z2.object({ outcome: z2.literal("aborted"), ...ObservationFields }),
762
- z2.object({
763
- outcome: z2.literal("realtime_rejected"),
1336
+ var TransportObservationSchema = z3.discriminatedUnion("outcome", [
1337
+ z3.object({ outcome: z3.literal("success"), ...ObservationFields }),
1338
+ z3.object({ outcome: z3.literal("validation_error"), ...ObservationFields }),
1339
+ z3.object({ outcome: z3.literal("domain_error"), ...ObservationFields }),
1340
+ z3.object({ outcome: z3.literal("aborted"), ...ObservationFields }),
1341
+ z3.object({
1342
+ outcome: z3.literal("realtime_rejected"),
764
1343
  ...ObservationFields,
765
1344
  rejection: RealtimeRejectionObservationSchema
766
1345
  }),
767
- z2.object({
768
- outcome: z2.literal("disconnected"),
1346
+ z3.object({
1347
+ outcome: z3.literal("disconnected"),
769
1348
  ...ObservationFields,
770
1349
  disconnect: RealtimeDisconnectObservationSchema
771
1350
  }),
772
- z2.object({ outcome: z2.literal("timeout"), ...ObservationFields })
1351
+ z3.object({ outcome: z3.literal("timeout"), ...ObservationFields })
773
1352
  ]);
774
1353
  function scenarioOutcome(scenario) {
775
1354
  if (scenario === "event" || scenario === "acknowledgement")
@@ -1081,6 +1660,12 @@ function createHandlerTestClients(config) {
1081
1660
  return createClients(config.contracts, createTestClientConfig(config), config.contractConfig);
1082
1661
  }
1083
1662
  export {
1663
+ ManagedResourceConformanceError,
1664
+ ManagedResourceConformancePhaseSchema,
1665
+ ManagedResourceConformanceScenarioIdSchema,
1666
+ ManagedResourceConformanceScenarioSchema,
1667
+ ManagedResourceConformanceTraceEntrySchema,
1668
+ ManagedResourceConformanceTraceOutcomeSchema,
1084
1669
  RealtimeDisconnectObservationSchema,
1085
1670
  RealtimeRejectionObservationSchema,
1086
1671
  SurfaceManifestExtensionSchema,
@@ -1103,6 +1688,7 @@ export {
1103
1688
  createRealtimeProbeDriver,
1104
1689
  defineRealtimeProbe,
1105
1690
  runAgentStoreConformance,
1691
+ runManagedResourceConformance,
1106
1692
  runSurfaceProbes,
1107
1693
  serializeSurfaceValue
1108
1694
  };