@fungi.computer/puffball 0.0.0-stage → 0.0.1

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 (110) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +443 -2
  3. package/dist/app-api.d.ts +7 -0
  4. package/dist/app-api.js +61 -0
  5. package/dist/app-backend-binding-vocabulary.d.ts +299 -0
  6. package/dist/app-backend-binding-vocabulary.js +257 -0
  7. package/dist/app-backend-bindings.d.ts +21 -0
  8. package/dist/app-backend-bindings.js +47 -0
  9. package/dist/app-declaration.d.ts +46 -0
  10. package/dist/app-declaration.js +115 -0
  11. package/dist/app-event-facts.d.ts +40 -0
  12. package/dist/app-event-facts.js +28 -0
  13. package/dist/app-method-context.d.ts +109 -0
  14. package/dist/app-method-context.js +9 -0
  15. package/dist/app-method-runtime.d.ts +86 -0
  16. package/dist/app-method-runtime.js +122 -0
  17. package/dist/app-origin-paths.d.ts +6 -0
  18. package/dist/app-origin-paths.js +6 -0
  19. package/dist/app-vocabulary.d.ts +189 -0
  20. package/dist/app-vocabulary.js +103 -0
  21. package/dist/app.d.ts +379 -0
  22. package/dist/app.js +71 -0
  23. package/dist/artifact-dispositions.d.ts +115 -0
  24. package/dist/artifact-dispositions.js +234 -0
  25. package/dist/assets.d.ts +54 -0
  26. package/dist/assets.js +89 -0
  27. package/dist/backend.d.ts +41 -0
  28. package/dist/backend.js +29 -0
  29. package/dist/background-replay.d.ts +115 -0
  30. package/dist/background-replay.js +175 -0
  31. package/dist/buyer-references.d.ts +69 -0
  32. package/dist/buyer-references.js +154 -0
  33. package/dist/cloudflare-assets.d.ts +48 -0
  34. package/dist/cloudflare-assets.js +105 -0
  35. package/dist/cloudflare.d.ts +254 -0
  36. package/dist/cloudflare.js +1619 -0
  37. package/dist/effect.d.ts +73 -0
  38. package/dist/effect.js +325 -0
  39. package/dist/git-representation.d.ts +13 -0
  40. package/dist/git-representation.js +16 -0
  41. package/dist/inbox.d.ts +132 -0
  42. package/dist/inbox.js +81 -0
  43. package/dist/index.d.ts +31 -0
  44. package/dist/index.js +85 -0
  45. package/dist/internal/app-description.d.ts +14 -0
  46. package/dist/internal/app-description.js +98 -0
  47. package/dist/internal/app-release-artifact-reader.d.ts +26 -0
  48. package/dist/internal/app-release-artifact-reader.js +1 -0
  49. package/dist/internal/app-release-contract.d.ts +540 -0
  50. package/dist/internal/app-release-contract.js +169 -0
  51. package/dist/internal/app-release-schema.d.ts +1676 -0
  52. package/dist/internal/app-release-schema.js +386 -0
  53. package/dist/internal/app-release-store.d.ts +47 -0
  54. package/dist/internal/app-release-store.js +1 -0
  55. package/dist/internal/background-sync.d.ts +7 -0
  56. package/dist/internal/background-sync.js +10 -0
  57. package/dist/internal/deterministic-memory-app-release-store.d.ts +14 -0
  58. package/dist/internal/deterministic-memory-app-release-store.js +319 -0
  59. package/dist/internal/failure.d.ts +2 -0
  60. package/dist/internal/failure.js +9 -0
  61. package/dist/internal/immutable-release-material.d.ts +3 -0
  62. package/dist/internal/immutable-release-material.js +13 -0
  63. package/dist/internal/promise-app-release-store-adapter.d.ts +9 -0
  64. package/dist/internal/promise-app-release-store-adapter.js +65 -0
  65. package/dist/internal/promise-artifact-reader-adapter.d.ts +9 -0
  66. package/dist/internal/promise-artifact-reader-adapter.js +18 -0
  67. package/dist/internal/public-route-sql.d.ts +10 -0
  68. package/dist/internal/public-route-sql.js +239 -0
  69. package/dist/internal/public-website-authority.d.ts +338 -0
  70. package/dist/internal/public-website-authority.js +50 -0
  71. package/dist/internal/public-website-delivery.d.ts +59 -0
  72. package/dist/internal/public-website-delivery.js +232 -0
  73. package/dist/internal/public-website-fetch.d.ts +8 -0
  74. package/dist/internal/public-website-fetch.js +54 -0
  75. package/dist/internal/release-transition-material.d.ts +3 -0
  76. package/dist/internal/release-transition-material.js +11 -0
  77. package/dist/internal/verified-artifact-snapshot.d.ts +41 -0
  78. package/dist/internal/verified-artifact-snapshot.js +91 -0
  79. package/dist/public-route-moderation.d.ts +83 -0
  80. package/dist/public-route-moderation.js +236 -0
  81. package/dist/public-route-policy.d.ts +51 -0
  82. package/dist/public-route-policy.js +88 -0
  83. package/dist/public-routes.d.ts +34 -0
  84. package/dist/public-routes.js +136 -0
  85. package/dist/publisher.d.ts +61 -0
  86. package/dist/publisher.js +16 -0
  87. package/dist/safety-takedown.d.ts +56 -0
  88. package/dist/safety-takedown.js +146 -0
  89. package/dist/seller-closure-notice.d.ts +14 -0
  90. package/dist/seller-closure-notice.js +14 -0
  91. package/dist/seller-closure.d.ts +15 -0
  92. package/dist/seller-closure.js +31 -0
  93. package/dist/testing.d.ts +8 -0
  94. package/dist/testing.js +47 -0
  95. package/docs/api/README.md +26 -0
  96. package/docs/api/app-backend-binding-vocabulary.md +276 -0
  97. package/docs/api/app-backend-bindings.md +141 -0
  98. package/docs/api/app-event-facts.md +110 -0
  99. package/docs/api/app-method-context.md +166 -0
  100. package/docs/api/app-method-runtime.md +330 -0
  101. package/docs/api/backend.md +108 -0
  102. package/docs/api/background-replay.md +516 -0
  103. package/docs/api/cloudflare.md +1044 -0
  104. package/docs/api/effect.md +2429 -0
  105. package/docs/api/git-representation.md +56 -0
  106. package/docs/api/inbox.md +289 -0
  107. package/docs/api/index.md +2868 -0
  108. package/docs/api/publisher.md +147 -0
  109. package/docs/api/testing.md +31 -0
  110. package/package.json +158 -3
@@ -0,0 +1,73 @@
1
+ import * as Effect from "effect/Effect";
2
+ import type { ResolvedApp } from "./app.js";
3
+ import * as Result from "effect/Result";
4
+ import type { App, StableRoute, PublicSurface, PublicSurfaceDefinition, PublicWebsiteRepresentation, Release, ReleaseId, ReleaseTransition, TransitionReceipt, VerifiedArtifactRead, ResolvedAppMetadata } from "./internal/app-release-contract.js";
5
+ import { AppReleaseImmutableConflictError, AppReleaseArtifactDigestMismatchError, AppReleaseArtifactInvalidSnapshotError, AppReleaseCommandInputError, AppReleaseInvalidStoreResultError, AppReleaseArtifactMissingError, AppReleaseArtifactUnavailableError, AppReleaseTransitionIdempotencyConflictError, AppReleaseTransitionOwnershipConflictError, AppReleaseTransitionReleaseAbsentError, AppReleaseTransitionReleaseRevokedError, AppReleaseTransitionReleaseUnverifiedError, AppReleaseTransitionArtifactIncompatibleError, AppReleaseTransitionSurfaceAbsentError, AppReleaseOwnerAppAbsentError, AppReleaseSurfaceOwnerAppAbsentError, AppReleaseReleaseAbsentError, AppReleaseStoreUnavailableError, AppReleaseTransitionIdUnavailableError, AppReleasePublicWebsiteNotFoundError, AppReleasePublicWebsiteUnavailableError } from "./internal/app-release-contract.js";
6
+ import type { AppReleaseStore } from "./internal/app-release-store.js";
7
+ import type { AppReleaseArtifactReader } from "./internal/app-release-artifact-reader.js";
8
+ import { type ArtifactSnapshotPolicy } from "./internal/verified-artifact-snapshot.js";
9
+ import { type EligibleSurfaceRelease } from "./internal/public-website-delivery.js";
10
+ export type { AppReleaseStore, AppReleaseStorePortFailure, } from "./internal/app-release-store.js";
11
+ export { createDeterministicMemoryAppReleaseStore } from "./internal/deterministic-memory-app-release-store.js";
12
+ export { adaptPromiseAppReleaseStore } from "./internal/promise-app-release-store-adapter.js";
13
+ export type { AppReleaseArtifactReader, AppReleaseArtifactReaderUnavailable, } from "./internal/app-release-artifact-reader.js";
14
+ export { adaptPromiseArtifactReader } from "./internal/promise-artifact-reader-adapter.js";
15
+ export { appIdSchema, appReadStoreResultSchema, appReadSchema, appCreateStoreResultSchema, appCreateSchema, appReleaseStoreFailureSchema, appSchema, appTransitionIdSchema, artifactProvenanceSchema, artifactReadResultSchema, artifactRefSchema, decodeApp, decodeAppCreateStoreResult, decodeAppId, decodeAppTransitionId, decodeAppReadStoreResult, decodeArtifactReadResult, decodeArtifactRef, decodeIdempotencyKey, decodePublicSurface, decodePublicSurfaceCreateStoreResult, decodePublicSurfaceId, decodePublicSurfaceReadStoreResult, decodeRelease, decodeReleaseCreateStoreResult, decodeReleaseId, decodeReleaseReadStoreResult, decodeSha256Digest, decodeSourceRevision, decodeStableRoute, idempotencyKeySchema, publicSurfaceCreateSchema, publicSurfaceCreateStoreResultSchema, publicSurfaceIdSchema, publicSurfaceReadSchema, publicSurfaceReadStoreResultSchema, publicSurfaceSchema, releaseArtifactSchema, releaseCreateSchema, releaseCreateStoreResultSchema, releaseIdSchema, releaseReadSchema, releaseReadStoreResultSchema, releaseSchema, sha256DigestSchema, sourceRevisionSchema, stableRouteSchema, verifiedArtifactReadSchema, } from "./internal/app-release-schema.js";
16
+ /** Closed domain failures that the core maps from store outcomes. */
17
+ export type AppReleaseCoreFailure = AppReleaseImmutableConflictError | AppReleaseOwnerAppAbsentError | AppReleaseSurfaceOwnerAppAbsentError | AppReleaseStoreUnavailableError | AppReleaseReleaseAbsentError | AppReleaseArtifactUnavailableError | AppReleaseArtifactMissingError | AppReleaseArtifactDigestMismatchError | AppReleaseArtifactInvalidSnapshotError | AppReleaseCommandInputError | AppReleaseInvalidStoreResultError | AppReleaseTransitionIdempotencyConflictError | AppReleaseTransitionOwnershipConflictError | AppReleaseTransitionReleaseAbsentError | AppReleaseTransitionReleaseRevokedError | AppReleaseTransitionReleaseUnverifiedError | AppReleaseTransitionArtifactIncompatibleError | AppReleaseTransitionSurfaceAbsentError | AppReleaseTransitionIdUnavailableError | AppReleasePublicWebsiteNotFoundError | AppReleasePublicWebsiteUnavailableError;
18
+ /** Effect-native core for the narrow App and immutable Release catalog facts. */
19
+ export type AppReleaseCore = {
20
+ /** Create or observe one Global App through the owned store port. */
21
+ createApp(app: App): Effect.Effect<App, AppReleaseCoreFailure>;
22
+ /** Create or observe one immutable Release through the owned store port. */
23
+ createRelease(release: Release): Effect.Effect<Release, AppReleaseCoreFailure>;
24
+ /** Verify the immutable artifact bound to one Release. */
25
+ verifyReleaseArtifact(releaseId: ReleaseId): Effect.Effect<VerifiedArtifactRead, AppReleaseCoreFailure>;
26
+ /** Read and recheck one eligible snapshot before each authorized use. */
27
+ resolveEligibleReleaseArtifact(releaseId: ReleaseId): Effect.Effect<VerifiedArtifactRead & {
28
+ readonly release: Release;
29
+ }, AppReleaseCoreFailure>;
30
+ /** Resolve UI and backend together, checking eligibility around all child reads. */
31
+ resolveEligibleApp(releaseId: ReleaseId): Effect.Effect<ResolvedApp, AppReleaseCoreFailure>;
32
+ /** Resolve verified App metadata without reading any artifact body. */
33
+ resolveEligibleAppMetadata(releaseId: ReleaseId): Effect.Effect<ResolvedAppMetadata, AppReleaseCoreFailure>;
34
+ /** Resolve one selected Surface's current metadata authority without bodies. */
35
+ resolvePublicSurfaceAuthority(stableRoute: StableRoute): Effect.Effect<import("./internal/app-release-contract.js").PublicSurfaceAuthority, AppReleaseCoreFailure>;
36
+ /** Resolve one previously selected Release's current metadata authority. */
37
+ resolvePublicSurfaceAuthorityVersion(stableRoute: StableRoute, releaseId: ReleaseId): Effect.Effect<import("./internal/app-release-contract.js").PublicSurfaceAuthority, AppReleaseCoreFailure>;
38
+ /** Resolve a multi-file root through existing Surface selection and eligibility. */
39
+ resolvePublicAssetBuild(stableRoute: StableRoute): Effect.Effect<import("./internal/app-release-contract.js").PublicAssetBuildRepresentation, AppReleaseCoreFailure>;
40
+ /** Create or observe one App-owned anonymous public Surface. */
41
+ createPublicSurface(surface: PublicSurfaceDefinition): Effect.Effect<PublicSurface, AppReleaseCoreFailure>;
42
+ /** Apply one owner-issued transition with an idempotent terminal receipt. */
43
+ transition(request: ReleaseTransition): Effect.Effect<TransitionReceipt, AppReleaseCoreFailure>;
44
+ /** Resolve the current eligible Surface Release without reading artifact bytes. */
45
+ resolveEligibleSurfaceRelease(stableRoute: StableRoute): Effect.Effect<EligibleSurfaceRelease, AppReleasePublicWebsiteNotFoundError | AppReleasePublicWebsiteUnavailableError>;
46
+ /**
47
+ * Read up to 100 current Surface selections in one atomic metadata batch.
48
+ * Each entry reports its eligibility; failure of the read fails the Effect.
49
+ */
50
+ resolveEligibleSurfaceReleases(stableRoutes: readonly StableRoute[]): Effect.Effect<readonly Result.Result<EligibleSurfaceRelease, AppReleasePublicWebsiteNotFoundError | AppReleasePublicWebsiteUnavailableError>[], AppReleasePublicWebsiteNotFoundError | AppReleasePublicWebsiteUnavailableError>;
51
+ /** Resolve one eligible Release previously selected on this Surface. */
52
+ resolveEligibleSurfaceVersion(stableRoute: StableRoute, releaseId: ReleaseId): Effect.Effect<EligibleSurfaceRelease, AppReleasePublicWebsiteNotFoundError | AppReleasePublicWebsiteUnavailableError>;
53
+ /** Resolve one stable route's immutable Surface owner without selecting a Release. */
54
+ resolvePublicSurface(stableRoute: StableRoute): Effect.Effect<PublicSurface, AppReleasePublicWebsiteNotFoundError | AppReleasePublicWebsiteUnavailableError>;
55
+ /** Resolve the current eligible immutable website representation. */
56
+ resolvePublicWebsite(stableRoute: import("./internal/app-release-contract.js").StableRoute): Effect.Effect<PublicWebsiteRepresentation, AppReleasePublicWebsiteNotFoundError | AppReleasePublicWebsiteUnavailableError>;
57
+ /** Resolve a pinned eligible immutable website representation. */
58
+ resolvePublicWebsiteVersion(stableRoute: import("./internal/app-release-contract.js").StableRoute, releaseId: ReleaseId): Effect.Effect<PublicWebsiteRepresentation, AppReleasePublicWebsiteNotFoundError | AppReleasePublicWebsiteUnavailableError>;
59
+ /** Resolve a pinned asset-build root through Surface authority. */
60
+ resolvePublicAssetBuildVersion(stableRoute: import("./internal/app-release-contract.js").StableRoute, releaseId: ReleaseId): Effect.Effect<import("./internal/app-release-contract.js").PublicAssetBuildRepresentation, AppReleasePublicWebsiteNotFoundError | AppReleasePublicWebsiteUnavailableError>;
61
+ };
62
+ /**
63
+ * Compose the Effect-native App Release core.
64
+ *
65
+ * A Release is first compared with its existing immutable record. Only an
66
+ * absent Release requires an owner App lookup and an atomic create-or-observe.
67
+ * Every closed store failure is mapped at this boundary.
68
+ */
69
+ export declare function createAppReleaseCore(options: {
70
+ readonly store: AppReleaseStore;
71
+ readonly artifactReader: AppReleaseArtifactReader;
72
+ readonly artifactPolicy?: ArtifactSnapshotPolicy;
73
+ }): AppReleaseCore;
package/dist/effect.js ADDED
@@ -0,0 +1,325 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { readAppDescription, readVerifiedAppDescription, } from "./internal/app-description.js";
3
+ import * as Result from "effect/Result";
4
+ import { CairnGenerationError } from "@fungi.computer/cairn/id";
5
+ import { AppReleaseImmutableConflictError, AppReleaseArtifactDigestMismatchError, AppReleaseArtifactInvalidSnapshotError, AppReleaseCommandInputError, AppReleaseInvalidStoreResultError, AppReleaseArtifactMissingError, AppReleaseArtifactUnavailableError, AppReleaseTransitionIdempotencyConflictError, AppReleaseTransitionOwnershipConflictError, AppReleaseTransitionReleaseAbsentError, AppReleaseTransitionReleaseRevokedError, AppReleaseTransitionReleaseUnverifiedError, AppReleaseTransitionArtifactIncompatibleError, AppReleaseTransitionSurfaceAbsentError, AppReleaseOwnerAppAbsentError, AppReleaseSurfaceOwnerAppAbsentError, AppReleaseReleaseAbsentError, AppReleaseStoreUnavailableError, AppReleaseTransitionIdUnavailableError, AppReleasePublicWebsiteNotFoundError, AppReleasePublicWebsiteUnavailableError, } from "./internal/app-release-contract.js";
6
+ import { decodeApp, decodePublicSurfaceDefinition, decodeRelease, decodeReleaseId, decodeReleaseTransition, decodeStableRoute, appTransitionIdCodec, } from "./internal/app-release-schema.js";
7
+ import { createArtifactSnapshotPolicy, DEFAULT_ARTIFACT_SNAPSHOT_POLICY, readVerifiedArtifactSnapshot, } from "./internal/verified-artifact-snapshot.js";
8
+ import { createEligibleSurfaceReleaseResolver, createEligibleSurfaceReleasesResolver, createEligibleSurfaceVersionResolver, createPublicSurfaceAuthorityResolver, createPublicSurfaceAuthorityVersionResolver, createPublicSurfaceResolver, createPublicWebsiteResolver, createPublicWebsiteVersionResolver, } from "./internal/public-website-delivery.js";
9
+ import { sameImmutableReleaseMaterial } from "./internal/immutable-release-material.js";
10
+ export { createDeterministicMemoryAppReleaseStore } from "./internal/deterministic-memory-app-release-store.js";
11
+ export { adaptPromiseAppReleaseStore } from "./internal/promise-app-release-store-adapter.js";
12
+ export { adaptPromiseArtifactReader } from "./internal/promise-artifact-reader-adapter.js";
13
+ export { appIdSchema, appReadStoreResultSchema, appReadSchema, appCreateStoreResultSchema, appCreateSchema, appReleaseStoreFailureSchema, appSchema, appTransitionIdSchema, artifactProvenanceSchema, artifactReadResultSchema, artifactRefSchema, decodeApp, decodeAppCreateStoreResult, decodeAppId, decodeAppTransitionId, decodeAppReadStoreResult, decodeArtifactReadResult, decodeArtifactRef, decodeIdempotencyKey, decodePublicSurface, decodePublicSurfaceCreateStoreResult, decodePublicSurfaceId, decodePublicSurfaceReadStoreResult, decodeRelease, decodeReleaseCreateStoreResult, decodeReleaseId, decodeReleaseReadStoreResult, decodeSha256Digest, decodeSourceRevision, decodeStableRoute, idempotencyKeySchema, publicSurfaceCreateSchema, publicSurfaceCreateStoreResultSchema, publicSurfaceIdSchema, publicSurfaceReadSchema, publicSurfaceReadStoreResultSchema, publicSurfaceSchema, releaseArtifactSchema, releaseCreateSchema, releaseCreateStoreResultSchema, releaseIdSchema, releaseReadSchema, releaseReadStoreResultSchema, releaseSchema, sha256DigestSchema, sourceRevisionSchema, stableRouteSchema, verifiedArtifactReadSchema, } from "./internal/app-release-schema.js";
14
+ /**
15
+ * Compose the Effect-native App Release core.
16
+ *
17
+ * A Release is first compared with its existing immutable record. Only an
18
+ * absent Release requires an owner App lookup and an atomic create-or-observe.
19
+ * Every closed store failure is mapped at this boundary.
20
+ */
21
+ export function createAppReleaseCore(options) {
22
+ const artifactPolicy = options.artifactPolicy === undefined
23
+ ? DEFAULT_ARTIFACT_SNAPSHOT_POLICY
24
+ : createArtifactSnapshotPolicy(options.artifactPolicy.maxBytes, options.artifactPolicy.maxManifestBytes);
25
+ const generateTransitionId = () => appTransitionIdCodec.generate();
26
+ const resolvePublicWebsite = createPublicWebsiteResolver({
27
+ mediaType: "text/html; charset=utf-8",
28
+ ...options,
29
+ artifactPolicy,
30
+ });
31
+ const resolvePublicWebsiteVersion = createPublicWebsiteVersionResolver({
32
+ mediaType: "text/html; charset=utf-8",
33
+ ...options,
34
+ artifactPolicy,
35
+ });
36
+ const resolveEligibleSurfaceRelease = createEligibleSurfaceReleaseResolver({
37
+ store: options.store,
38
+ });
39
+ const resolvePublicSurfaceAuthority = createPublicSurfaceAuthorityResolver({
40
+ store: options.store,
41
+ });
42
+ const resolvePublicSurfaceAuthorityVersion = createPublicSurfaceAuthorityVersionResolver({ store: options.store });
43
+ const resolvePublicAssetBuild = createPublicWebsiteResolver({
44
+ store: options.store,
45
+ artifactReader: options.artifactReader,
46
+ artifactPolicy,
47
+ mediaType: "application/vnd.fungi.app-assets+json",
48
+ });
49
+ const resolvePublicAssetBuildVersion = createPublicWebsiteVersionResolver({
50
+ store: options.store,
51
+ artifactReader: options.artifactReader,
52
+ artifactPolicy,
53
+ mediaType: "application/vnd.fungi.app-assets+json",
54
+ });
55
+ return {
56
+ createApp: (app) => decodeCommand(decodeApp(app), "createApp").pipe(Effect.flatMap((decoded) => options.store.createAppIfAbsent(decoded).pipe(Effect.mapError(storePortFailure), Effect.map((outcome) => outcome.app)))),
57
+ createRelease: (release) => decodeCommand(decodeRelease(release), "createRelease").pipe(Effect.flatMap((decoded) => createRelease(options.store, decoded))),
58
+ verifyReleaseArtifact: (releaseId) => decodeCommand(decodeReleaseId(releaseId), "verifyReleaseArtifact").pipe(Effect.flatMap((decoded) => verifyReleaseArtifact(options.store, options.artifactReader, decoded, artifactPolicy))),
59
+ resolveEligibleReleaseArtifact: (releaseId) => resolveEligibleArtifact(options, releaseId, artifactPolicy, (snapshot) => Effect.succeed({ kind: "verified", ...snapshot })),
60
+ resolveEligibleApp: (releaseId) => resolveEligibleArtifact(options, releaseId, artifactPolicy, (snapshot) => readAppDescription(options.artifactReader, snapshot, artifactPolicy).pipe(Effect.map((description) => ({
61
+ ...description,
62
+ digest: snapshot.digest,
63
+ })))),
64
+ resolveEligibleAppMetadata: (releaseId) => decodeCommand(decodeReleaseId(releaseId), "resolveEligibleAppMetadata").pipe(Effect.flatMap((decoded) => resolveEligibleAppMetadata(options.store, decoded))),
65
+ resolvePublicSurfaceAuthority: (stableRoute) => decodeCommand(decodeStableRoute(stableRoute), "resolvePublicSurfaceAuthority").pipe(Effect.flatMap((decoded) => resolvePublicSurfaceAuthority(decoded))),
66
+ resolvePublicSurfaceAuthorityVersion: (stableRoute, releaseId) => decodeCommand(decodeStableRoute(stableRoute), "resolvePublicSurfaceAuthorityVersion").pipe(Effect.flatMap((decodedRoute) => decodeCommand(decodeReleaseId(releaseId), "resolvePublicSurfaceAuthorityVersion").pipe(Effect.flatMap((decodedRelease) => resolvePublicSurfaceAuthorityVersion(decodedRoute, decodedRelease))))),
67
+ createPublicSurface: (surface) => decodeCommand(decodePublicSurfaceDefinition(surface), "createPublicSurface").pipe(Effect.flatMap((decoded) => createPublicSurface(options.store, decoded))),
68
+ transition: (request) => decodeCommand(decodeReleaseTransition(request), "transition").pipe(Effect.flatMap((decoded) => applyTransition(options.store, options.artifactReader, artifactPolicy, decoded, generateTransitionId))),
69
+ resolveEligibleSurfaceRelease,
70
+ resolveEligibleSurfaceReleases: createEligibleSurfaceReleasesResolver({
71
+ store: options.store,
72
+ }),
73
+ resolveEligibleSurfaceVersion: createEligibleSurfaceVersionResolver({
74
+ store: options.store,
75
+ }),
76
+ resolvePublicSurface: createPublicSurfaceResolver({
77
+ store: options.store,
78
+ }),
79
+ resolvePublicWebsite,
80
+ resolvePublicWebsiteVersion,
81
+ resolvePublicAssetBuild,
82
+ resolvePublicAssetBuildVersion,
83
+ };
84
+ }
85
+ function resolveEligibleArtifact(options, releaseId, artifactPolicy, resolve) {
86
+ return Effect.gen(function* () {
87
+ const decoded = yield* decodeCommand(decodeReleaseId(releaseId), "resolveEligibleReleaseArtifact");
88
+ const readLifecycle = options.store.readReleaseLifecycle;
89
+ if (readLifecycle === undefined)
90
+ return yield* Effect.fail(storeUnavailable());
91
+ const eligibleRelease = Effect.gen(function* () {
92
+ const lifecycle = yield* readLifecycle(decoded).pipe(Effect.mapError(storePortFailure));
93
+ if (lifecycle.kind === "absent")
94
+ return yield* Effect.fail(new AppReleaseReleaseAbsentError());
95
+ if (lifecycle.release.releaseId !== decoded)
96
+ return yield* Effect.fail(invalidStoreResult());
97
+ if (lifecycle.verification.kind !== "verified" ||
98
+ lifecycle.verification.digest !==
99
+ lifecycle.release.artifact.artifactDigest)
100
+ return yield* Effect.fail(new AppReleaseTransitionReleaseUnverifiedError());
101
+ if (lifecycle.revocation.kind !== "active")
102
+ return yield* Effect.fail(new AppReleaseTransitionReleaseRevokedError());
103
+ return lifecycle.release;
104
+ });
105
+ const first = yield* eligibleRelease;
106
+ const snapshot = yield* readVerifiedArtifactSnapshot(options.artifactReader, {
107
+ artifactRef: first.artifact.artifactRef,
108
+ expectedDigest: first.artifact.artifactDigest,
109
+ }, artifactPolicy).pipe(Effect.mapError(artifactSnapshotFailure));
110
+ const value = yield* resolve(snapshot).pipe(Effect.mapError(artifactSnapshotFailure));
111
+ // Both observations must be verified with the exact digest and active;
112
+ // comparing immutable material then compares the entire eligible lifecycle.
113
+ const second = yield* eligibleRelease;
114
+ if (!sameImmutableReleaseMaterial(first, second)) {
115
+ return yield* Effect.fail(new AppReleaseImmutableConflictError());
116
+ }
117
+ return { ...value, release: first };
118
+ });
119
+ }
120
+ function verifyReleaseArtifact(store, artifactReader, releaseId, artifactPolicy) {
121
+ return store.readRelease(releaseId).pipe(Effect.mapError(storePortFailure), Effect.flatMap((read) => {
122
+ if (read.kind === "absent") {
123
+ return Effect.fail(new AppReleaseReleaseAbsentError());
124
+ }
125
+ const expectedDigest = read.release.artifact.artifactDigest;
126
+ return readVerifiedArtifactSnapshot(artifactReader, {
127
+ artifactRef: read.release.artifact.artifactRef,
128
+ expectedDigest,
129
+ verification: "publication",
130
+ }, artifactPolicy).pipe(Effect.mapError(artifactSnapshotFailure), Effect.flatMap((snapshot) => readVerifiedAppDescription(artifactReader, snapshot, artifactPolicy, "publication").pipe(Effect.mapError(artifactSnapshotFailure), Effect.flatMap(({ metadata }) => store.recordReleaseVerification === undefined
131
+ ? Effect.fail(storeUnavailable())
132
+ : store
133
+ .recordReleaseVerification({
134
+ releaseId,
135
+ digest: snapshot.digest,
136
+ mediaType: "application/vnd.fungi.app+json",
137
+ description: metadata,
138
+ })
139
+ .pipe(Effect.mapError(storePortFailure), Effect.map(() => ({
140
+ kind: "verified",
141
+ ...snapshot,
142
+ })))))));
143
+ }));
144
+ }
145
+ function resolveEligibleAppMetadata(store, releaseId) {
146
+ const readLifecycle = store.readReleaseLifecycle;
147
+ if (readLifecycle === undefined)
148
+ return Effect.fail(storeUnavailable());
149
+ return readLifecycle(releaseId).pipe(Effect.mapError(storePortFailure), Effect.flatMap((lifecycle) => {
150
+ if (lifecycle.kind === "absent") {
151
+ return Effect.fail(new AppReleaseReleaseAbsentError());
152
+ }
153
+ if (lifecycle.release.releaseId !== releaseId) {
154
+ return Effect.fail(invalidStoreResult());
155
+ }
156
+ if (lifecycle.verification.kind !== "verified" ||
157
+ lifecycle.verification.digest !==
158
+ lifecycle.release.artifact.artifactDigest) {
159
+ return Effect.fail(new AppReleaseTransitionReleaseUnverifiedError());
160
+ }
161
+ if (lifecycle.revocation.kind !== "active") {
162
+ return Effect.fail(new AppReleaseTransitionReleaseRevokedError());
163
+ }
164
+ return Effect.succeed({
165
+ release: lifecycle.release,
166
+ digest: lifecycle.verification.digest,
167
+ description: lifecycle.verification.description,
168
+ });
169
+ }));
170
+ }
171
+ function createRelease(store, requested) {
172
+ return store.readRelease(requested.releaseId).pipe(Effect.mapError(storePortFailure), Effect.flatMap((read) => {
173
+ if (read.kind === "found")
174
+ return compareImmutable(requested, read.release);
175
+ return store.readApp(requested.appId).pipe(Effect.mapError(storePortFailure), Effect.flatMap((owner) => {
176
+ if (owner.kind === "absent") {
177
+ return Effect.fail(new AppReleaseOwnerAppAbsentError(requested.appId));
178
+ }
179
+ return store.createReleaseIfAbsent(requested).pipe(Effect.mapError(storePortFailure), Effect.flatMap((created) => compareImmutable(requested, created.release)));
180
+ }));
181
+ }));
182
+ }
183
+ function createPublicSurface(store, surface) {
184
+ return store.readApp(surface.appId).pipe(Effect.mapError(storePortFailure), Effect.flatMap((owner) => {
185
+ if (owner.kind === "absent") {
186
+ return Effect.fail(new AppReleaseSurfaceOwnerAppAbsentError(surface.appId));
187
+ }
188
+ return store.createPublicSurfaceIfAbsent(surface).pipe(Effect.mapError(storePortFailure), Effect.flatMap((outcome) => outcome.kind === "conflict"
189
+ ? Effect.fail(new AppReleaseImmutableConflictError())
190
+ : comparePublicSurface(surface, outcome.surface)));
191
+ }));
192
+ }
193
+ function applyTransition(store, artifactReader, artifactPolicy, request, nextTransitionId) {
194
+ const readTransition = store.readTransition;
195
+ if (readTransition === undefined)
196
+ return Effect.fail(storeUnavailable());
197
+ return readTransition(request).pipe(Effect.mapError(storePortFailure), Effect.flatMap((lookup) => {
198
+ if (lookup.kind === "found")
199
+ return Effect.succeed(lookup.receipt);
200
+ if (lookup.kind === "conflict") {
201
+ return Effect.fail(new AppReleaseTransitionIdempotencyConflictError());
202
+ }
203
+ const readLifecycle = store.readReleaseLifecycle;
204
+ if (readLifecycle === undefined)
205
+ return Effect.fail(storeUnavailable());
206
+ return readLifecycle(request.releaseId).pipe(Effect.mapError(storePortFailure), Effect.flatMap((releaseLifecycle) => {
207
+ if (releaseLifecycle.kind === "absent") {
208
+ return Effect.fail(new AppReleaseTransitionReleaseAbsentError());
209
+ }
210
+ if (request.kind !== "revocation") {
211
+ if (releaseLifecycle.verification.kind !== "verified") {
212
+ return Effect.fail(new AppReleaseTransitionReleaseUnverifiedError());
213
+ }
214
+ if (releaseLifecycle.verification.digest !==
215
+ releaseLifecycle.release.artifact.artifactDigest) {
216
+ return Effect.fail(new AppReleaseTransitionReleaseUnverifiedError());
217
+ }
218
+ if (releaseLifecycle.revocation.kind === "revoked") {
219
+ return Effect.fail(new AppReleaseTransitionReleaseRevokedError());
220
+ }
221
+ }
222
+ if (request.kind === "revocation") {
223
+ return persistTransition(store, request, nextTransitionId);
224
+ }
225
+ return store.readPublicSurface(request.publicSurfaceId).pipe(Effect.mapError(storePortFailure), Effect.flatMap((surface) => {
226
+ if (surface.kind === "absent") {
227
+ return Effect.fail(new AppReleaseTransitionSurfaceAbsentError());
228
+ }
229
+ if (surface.surface.appId !== releaseLifecycle.release.appId) {
230
+ return Effect.fail(new AppReleaseTransitionOwnershipConflictError());
231
+ }
232
+ return readVerifiedArtifactSnapshot(artifactReader, {
233
+ artifactRef: releaseLifecycle.release.artifact.artifactRef,
234
+ expectedDigest: releaseLifecycle.release.artifact.artifactDigest,
235
+ verification: "publication",
236
+ }, artifactPolicy).pipe(Effect.mapError(artifactSnapshotFailure), Effect.flatMap((artifact) => {
237
+ if (artifact.mediaType === "application/vnd.fungi.app+json") {
238
+ return readAppDescription(artifactReader, artifact, artifactPolicy, "publication").pipe(Effect.mapError(artifactSnapshotFailure), Effect.flatMap((app) => app.ui === null
239
+ ? Effect.fail(new AppReleaseTransitionArtifactIncompatibleError())
240
+ : persistTransition(store, request, nextTransitionId)));
241
+ }
242
+ return artifact.mediaType === "text/html; charset=utf-8" ||
243
+ artifact.mediaType ===
244
+ "application/vnd.fungi.app-assets+json"
245
+ ? persistTransition(store, request, nextTransitionId)
246
+ : Effect.fail(new AppReleaseTransitionArtifactIncompatibleError());
247
+ }));
248
+ }));
249
+ }));
250
+ }));
251
+ }
252
+ function persistTransition(store, request, nextTransitionId) {
253
+ const persist = store.persistTransition;
254
+ if (persist === undefined)
255
+ return Effect.fail(storeUnavailable());
256
+ return Effect.try({
257
+ try: nextTransitionId,
258
+ catch: (error) => {
259
+ if (error instanceof CairnGenerationError) {
260
+ return new AppReleaseTransitionIdUnavailableError();
261
+ }
262
+ throw error;
263
+ },
264
+ }).pipe(Effect.flatMap((transitionId) => persist(request, { transition: request, transitionId }).pipe(Effect.mapError(storePortFailure), Effect.flatMap((outcome) => {
265
+ if (outcome.kind === "conflict") {
266
+ return Effect.fail(new AppReleaseTransitionIdempotencyConflictError());
267
+ }
268
+ if (outcome.kind === "identity-collision") {
269
+ return Effect.fail(new AppReleaseTransitionIdUnavailableError());
270
+ }
271
+ if (outcome.kind === "lifecycle-conflict") {
272
+ return Effect.fail(new AppReleaseTransitionReleaseRevokedError());
273
+ }
274
+ return Effect.succeed(outcome.receipt);
275
+ }))));
276
+ }
277
+ function compareImmutable(requested, existing) {
278
+ return sameImmutableReleaseMaterial(requested, existing)
279
+ ? Effect.succeed(existing)
280
+ : Effect.fail(new AppReleaseImmutableConflictError());
281
+ }
282
+ function comparePublicSurface(requested, existing) {
283
+ return requested.appId === existing.appId &&
284
+ requested.stableRoute === existing.stableRoute
285
+ ? Effect.succeed(existing)
286
+ : Effect.fail(new AppReleaseImmutableConflictError());
287
+ }
288
+ function storeUnavailable() {
289
+ return new AppReleaseStoreUnavailableError();
290
+ }
291
+ function invalidStoreResult() {
292
+ return new AppReleaseInvalidStoreResultError();
293
+ }
294
+ function storePortFailure(failure) {
295
+ return failure.kind === "unavailable"
296
+ ? storeUnavailable()
297
+ : invalidStoreResult();
298
+ }
299
+ function artifactUnavailable() {
300
+ return new AppReleaseArtifactUnavailableError();
301
+ }
302
+ function artifactInvalidSnapshot() {
303
+ return new AppReleaseArtifactInvalidSnapshotError();
304
+ }
305
+ function artifactSnapshotFailure(failure) {
306
+ switch (failure.kind) {
307
+ case "unavailable":
308
+ return artifactUnavailable();
309
+ case "missing":
310
+ return new AppReleaseArtifactMissingError();
311
+ case "digest-mismatch":
312
+ return new AppReleaseArtifactDigestMismatchError();
313
+ case "invalid-snapshot":
314
+ return artifactInvalidSnapshot();
315
+ }
316
+ }
317
+ function commandInput(commandName) {
318
+ return new AppReleaseCommandInputError(commandName);
319
+ }
320
+ function decodeCommand(decoded, commandName) {
321
+ return Result.match(decoded, {
322
+ onFailure: () => Effect.fail(commandInput(commandName)),
323
+ onSuccess: Effect.succeed,
324
+ });
325
+ }
@@ -0,0 +1,13 @@
1
+ import * as z from "zod";
2
+ /** Local repository name; the host maps it into the installation's namespace. */
3
+ export declare const gitRepositoryNameSchema: z.ZodString;
4
+ /** A lowercase Git SHA-1 object ID, including the zero ID for creation/deletion. */
5
+ export declare const gitSha1Schema: z.ZodString;
6
+ /** One ref update, bounded before a host asks the App or forwards the push. */
7
+ export declare const gitRefUpdateSchema: z.ZodObject<{
8
+ ref: z.ZodString;
9
+ before: z.ZodString;
10
+ after: z.ZodString;
11
+ }, z.core.$strict>;
12
+ /** Host push event IDs include the Computer and local event identity. */
13
+ export declare const gitPushEventIdSchema: z.ZodString;
@@ -0,0 +1,16 @@
1
+ import * as z from "zod";
2
+ /** Local repository name; the host maps it into the installation's namespace. */
3
+ export const gitRepositoryNameSchema = z
4
+ .string()
5
+ .regex(/^[a-zA-Z0-9._-]{1,128}$/)
6
+ .refine((name) => name !== "." && name !== "..");
7
+ /** A lowercase Git SHA-1 object ID, including the zero ID for creation/deletion. */
8
+ export const gitSha1Schema = z.string().regex(/^[0-9a-f]{40}$/);
9
+ /** One ref update, bounded before a host asks the App or forwards the push. */
10
+ export const gitRefUpdateSchema = z.strictObject({
11
+ ref: z.string().startsWith("refs/").max(1024),
12
+ before: gitSha1Schema,
13
+ after: gitSha1Schema,
14
+ });
15
+ /** Host push event IDs include the Computer and local event identity. */
16
+ export const gitPushEventIdSchema = z.string().min(1).max(512);
@@ -0,0 +1,132 @@
1
+ import * as Schema from "effect/Schema";
2
+ /** One Session, projected by its Agent owner, with this member's visit. */
3
+ export declare const memberInboxSessionSchema: Schema.Union<readonly [Schema.Struct<{
4
+ readonly agentId: Schema.String;
5
+ readonly sessionId: Schema.String;
6
+ readonly title: Schema.String;
7
+ readonly triggerId: Schema.optionalKey<Schema.String>;
8
+ readonly createdAt: Schema.Number;
9
+ readonly lastActivityAt: Schema.Number;
10
+ readonly settledAt: Schema.optionalKey<Schema.Number>;
11
+ readonly visitedAt: Schema.optionalKey<Schema.Number>;
12
+ readonly state: Schema.Literals<readonly ["needs_you", "running", "errored", "done"]>;
13
+ }>, Schema.Struct<{
14
+ readonly agentId: Schema.String;
15
+ readonly sessionId: Schema.String;
16
+ readonly state: Schema.Literal<"unavailable">;
17
+ }>]>;
18
+ /** Parsed Session facts in the shared Inbox vocabulary. */
19
+ export type MemberInboxSession = typeof memberInboxSessionSchema.Type;
20
+ /** A review's engine identity, independent of its native delivery coordinates. */
21
+ export declare const workflowReviewKeySchema: Schema.Struct<{
22
+ readonly runId: Schema.String;
23
+ readonly nodeId: Schema.String;
24
+ readonly iteration: Schema.String;
25
+ }>;
26
+ /** The run, node and iteration that identify one human review. */
27
+ export type WorkflowReviewKey = typeof workflowReviewKeySchema.Type;
28
+ /** Facts delivered to an admitted reviewer by the run owner, never a fake Session. */
29
+ export declare const workflowReviewInboxItemSchema: Schema.Struct<{
30
+ readonly kind: Schema.Literal<"workflow-review">;
31
+ readonly key: Schema.Struct<{
32
+ readonly runId: Schema.String;
33
+ readonly nodeId: Schema.String;
34
+ readonly iteration: Schema.String;
35
+ }>;
36
+ readonly installationId: Schema.String;
37
+ /** Raw unpublished graphs have no immutable publication identity. */
38
+ readonly workflowId: Schema.NullOr<Schema.String>;
39
+ readonly workflowTitle: Schema.String;
40
+ readonly reviewTitle: Schema.String;
41
+ readonly reviewers: Schema.Literals<readonly ["runner", "owners"]>;
42
+ readonly show: Schema.$Array<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
43
+ readonly choices: Schema.optionalKey<Schema.$Array<Schema.String>>;
44
+ /** Actual insertion time and the stored deadline, retained on replay. */
45
+ readonly openedAt: Schema.Number;
46
+ readonly expiresAt: Schema.Number;
47
+ readonly state: Schema.Literals<readonly ["pending", "answered", "delivered", "timed_out", "closed"]>;
48
+ }>;
49
+ /** A reviewer-visible projection of a saved Workflow review. */
50
+ export type WorkflowReviewInboxItem = typeof workflowReviewInboxItemSchema.Type;
51
+ /** The INBOX wire has one vocabulary on every host and App client. */
52
+ export declare const memberInboxItemSchema: Schema.Union<readonly [Schema.Union<readonly [Schema.Struct<{
53
+ readonly agentId: Schema.String;
54
+ readonly sessionId: Schema.String;
55
+ readonly title: Schema.String;
56
+ readonly triggerId: Schema.optionalKey<Schema.String>;
57
+ readonly createdAt: Schema.Number;
58
+ readonly lastActivityAt: Schema.Number;
59
+ readonly settledAt: Schema.optionalKey<Schema.Number>;
60
+ readonly visitedAt: Schema.optionalKey<Schema.Number>;
61
+ readonly state: Schema.Literals<readonly ["needs_you", "running", "errored", "done"]>;
62
+ }>, Schema.Struct<{
63
+ readonly agentId: Schema.String;
64
+ readonly sessionId: Schema.String;
65
+ readonly state: Schema.Literal<"unavailable">;
66
+ }>]>, Schema.Struct<{
67
+ readonly kind: Schema.Literal<"workflow-review">;
68
+ readonly key: Schema.Struct<{
69
+ readonly runId: Schema.String;
70
+ readonly nodeId: Schema.String;
71
+ readonly iteration: Schema.String;
72
+ }>;
73
+ readonly installationId: Schema.String;
74
+ /** Raw unpublished graphs have no immutable publication identity. */
75
+ readonly workflowId: Schema.NullOr<Schema.String>;
76
+ readonly workflowTitle: Schema.String;
77
+ readonly reviewTitle: Schema.String;
78
+ readonly reviewers: Schema.Literals<readonly ["runner", "owners"]>;
79
+ readonly show: Schema.$Array<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
80
+ readonly choices: Schema.optionalKey<Schema.$Array<Schema.String>>;
81
+ /** Actual insertion time and the stored deadline, retained on replay. */
82
+ readonly openedAt: Schema.Number;
83
+ readonly expiresAt: Schema.Number;
84
+ readonly state: Schema.Literals<readonly ["pending", "answered", "delivered", "timed_out", "closed"]>;
85
+ }>]>;
86
+ /** An Inbox row is a Session or a human Workflow review. */
87
+ export type MemberInboxItem = typeof memberInboxItemSchema.Type;
88
+ /** A row replacement or an Agent-owned Session deletion. */
89
+ export declare const memberInboxDeltaItemSchema: Schema.Union<readonly [Schema.Union<readonly [Schema.Union<readonly [Schema.Struct<{
90
+ readonly agentId: Schema.String;
91
+ readonly sessionId: Schema.String;
92
+ readonly title: Schema.String;
93
+ readonly triggerId: Schema.optionalKey<Schema.String>;
94
+ readonly createdAt: Schema.Number;
95
+ readonly lastActivityAt: Schema.Number;
96
+ readonly settledAt: Schema.optionalKey<Schema.Number>;
97
+ readonly visitedAt: Schema.optionalKey<Schema.Number>;
98
+ readonly state: Schema.Literals<readonly ["needs_you", "running", "errored", "done"]>;
99
+ }>, Schema.Struct<{
100
+ readonly agentId: Schema.String;
101
+ readonly sessionId: Schema.String;
102
+ readonly state: Schema.Literal<"unavailable">;
103
+ }>]>, Schema.Struct<{
104
+ readonly kind: Schema.Literal<"workflow-review">;
105
+ readonly key: Schema.Struct<{
106
+ readonly runId: Schema.String;
107
+ readonly nodeId: Schema.String;
108
+ readonly iteration: Schema.String;
109
+ }>;
110
+ readonly installationId: Schema.String;
111
+ /** Raw unpublished graphs have no immutable publication identity. */
112
+ readonly workflowId: Schema.NullOr<Schema.String>;
113
+ readonly workflowTitle: Schema.String;
114
+ readonly reviewTitle: Schema.String;
115
+ readonly reviewers: Schema.Literals<readonly ["runner", "owners"]>;
116
+ readonly show: Schema.$Array<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
117
+ readonly choices: Schema.optionalKey<Schema.$Array<Schema.String>>;
118
+ /** Actual insertion time and the stored deadline, retained on replay. */
119
+ readonly openedAt: Schema.Number;
120
+ readonly expiresAt: Schema.Number;
121
+ readonly state: Schema.Literals<readonly ["pending", "answered", "delivered", "timed_out", "closed"]>;
122
+ }>]>, Schema.Struct<{
123
+ readonly agentId: Schema.String;
124
+ readonly sessionId: Schema.String;
125
+ readonly deleted: Schema.Literal<true>;
126
+ }>]>;
127
+ /** One record carried by an Inbox row delta. */
128
+ export type MemberInboxDeltaItem = typeof memberInboxDeltaItemSchema.Type;
129
+ /** Stable identity of one Session across Agents. */
130
+ export declare function memberInboxKey(agentId: string, sessionId: string): string;
131
+ /** Stable placement across review transitions; Map iterations remain distinct. */
132
+ export declare function memberInboxItemKey(item: MemberInboxDeltaItem): string;
package/dist/inbox.js ADDED
@@ -0,0 +1,81 @@
1
+ import * as Schema from "effect/Schema";
2
+ const identity = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(256));
3
+ const time = Schema.Number.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0));
4
+ /** One Session, projected by its Agent owner, with this member's visit. */
5
+ export const memberInboxSessionSchema = Schema.Union([
6
+ Schema.Struct({
7
+ agentId: identity,
8
+ sessionId: identity,
9
+ title: Schema.String.check(Schema.isMaxLength(4_096)),
10
+ triggerId: Schema.optionalKey(identity),
11
+ createdAt: time,
12
+ lastActivityAt: time,
13
+ settledAt: Schema.optionalKey(time),
14
+ visitedAt: Schema.optionalKey(time),
15
+ state: Schema.Literals(["needs_you", "running", "errored", "done"]),
16
+ }),
17
+ Schema.Struct({
18
+ agentId: identity,
19
+ sessionId: identity,
20
+ state: Schema.Literal("unavailable"),
21
+ }),
22
+ ]);
23
+ /** A review's engine identity, independent of its native delivery coordinates. */
24
+ export const workflowReviewKeySchema = Schema.Struct({
25
+ runId: identity,
26
+ nodeId: identity,
27
+ iteration: identity,
28
+ });
29
+ /** Facts delivered to an admitted reviewer by the run owner, never a fake Session. */
30
+ export const workflowReviewInboxItemSchema = Schema.Struct({
31
+ kind: Schema.Literal("workflow-review"),
32
+ key: workflowReviewKeySchema,
33
+ installationId: identity,
34
+ /** Raw unpublished graphs have no immutable publication identity. */
35
+ workflowId: Schema.NullOr(identity),
36
+ workflowTitle: Schema.String,
37
+ reviewTitle: Schema.String,
38
+ reviewers: Schema.Literals(["runner", "owners"]),
39
+ show: Schema.Array(Schema.Json),
40
+ choices: Schema.optionalKey(Schema.Array(identity)),
41
+ /** Actual insertion time and the stored deadline, retained on replay. */
42
+ openedAt: time,
43
+ expiresAt: time,
44
+ state: Schema.Literals([
45
+ "pending",
46
+ "answered",
47
+ "delivered",
48
+ "timed_out",
49
+ "closed",
50
+ ]),
51
+ });
52
+ /** The INBOX wire has one vocabulary on every host and App client. */
53
+ export const memberInboxItemSchema = Schema.Union([
54
+ memberInboxSessionSchema,
55
+ workflowReviewInboxItemSchema,
56
+ ]);
57
+ /** A row replacement or an Agent-owned Session deletion. */
58
+ export const memberInboxDeltaItemSchema = Schema.Union([
59
+ memberInboxItemSchema,
60
+ Schema.Struct({
61
+ agentId: identity,
62
+ sessionId: identity,
63
+ deleted: Schema.Literal(true),
64
+ }),
65
+ ]);
66
+ /** Stable identity of one Session across Agents. */
67
+ export function memberInboxKey(agentId, sessionId) {
68
+ return `${agentId}\u0000${sessionId}`;
69
+ }
70
+ /** Stable placement across review transitions; Map iterations remain distinct. */
71
+ export function memberInboxItemKey(item) {
72
+ return "kind" in item
73
+ ? JSON.stringify([
74
+ item.kind,
75
+ item.installationId,
76
+ item.key.runId,
77
+ item.key.nodeId,
78
+ item.key.iteration,
79
+ ])
80
+ : memberInboxKey(item.agentId, item.sessionId);
81
+ }