| ... | ... | @@ -20,6 +20,7 @@ import { CommandId, ProviderInstanceId } from "@t3tools/contracts"; |
| 20 | 20 | import { RelayClientTracer } from "@t3tools/shared/relayTracing"; |
| 21 | 21 | import { RELAY_ACTIVITY_PUBLISH_TYP, verifyRelayJwt } from "@t3tools/shared/relayJwt"; |
| 22 | 22 | import { describe, expect, it } from "@effect/vitest"; |
| 23 | import * as DateTime from "effect/DateTime"; |
| 23 | 24 | import * as Deferred from "effect/Deferred"; |
| 24 | 25 | import * as Effect from "effect/Effect"; |
| 25 | 26 | import * as Layer from "effect/Layer"; |
| ... | ... | @@ -325,6 +326,9 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { |
| 325 | 326 | const projectId = "project-1" as ProjectId; |
| 326 | 327 | const activeThreadId = "thread-active" as ThreadId; |
| 327 | 328 | const idleThreadId = "thread-idle" as ThreadId; |
| 329 | const oldCompletedId = "thread-old-completed" as ThreadId; |
| 330 | const newCompletedId = "thread-new-completed" as ThreadId; |
| 331 | const freshMessageId = "thread-fresh-message" as ThreadId; |
| 328 | 332 | |
| 329 | 333 | const baseThread = { |
| 330 | 334 | projectId, |
| ... | ... | @@ -351,6 +355,7 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { |
| 351 | 355 | expect( |
| 352 | 356 | AgentAwarenessRelay.resolveAgentAwarenessRelayActiveThreadIds({ |
| 353 | 357 | environmentId, |
| 358 | startedAt: Date.parse(now), |
| 354 | 359 | projects: [ |
| 355 | 360 | { |
| 356 | 361 | id: projectId, |
| ... | ... | @@ -374,6 +379,44 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { |
| 374 | 379 | ...baseThread, |
| 375 | 380 | id: idleThreadId, |
| 376 | 381 | }, |
| 382 | { |
| 383 | ...baseThread, |
| 384 | id: oldCompletedId, |
| 385 | latestTurn: { |
| 386 | turnId: "turn-old" as TurnId, |
| 387 | state: "completed", |
| 388 | requestedAt: "2026-05-24T00:00:00.000Z", |
| 389 | startedAt: "2026-05-24T00:00:00.000Z", |
| 390 | completedAt: "2026-05-24T00:01:00.000Z", |
| 391 | assistantMessageId: null, |
| 392 | }, |
| 393 | }, |
| 394 | { |
| 395 | ...baseThread, |
| 396 | id: newCompletedId, |
| 397 | latestTurn: { |
| 398 | turnId: "turn-new" as TurnId, |
| 399 | state: "completed", |
| 400 | requestedAt: "2026-05-25T00:00:01.000Z", |
| 401 | startedAt: "2026-05-25T00:00:01.000Z", |
| 402 | completedAt: "2026-05-25T00:00:02.000Z", |
| 403 | assistantMessageId: null, |
| 404 | }, |
| 405 | }, |
| 406 | { |
| 407 | ...baseThread, |
| 408 | id: freshMessageId, |
| 409 | latestUserMessageAt: "2026-05-25T00:00:01.000Z", |
| 410 | session: { |
| 411 | threadId: freshMessageId, |
| 412 | status: "ready", |
| 413 | providerName: "Codex", |
| 414 | runtimeMode: "full-access", |
| 415 | activeTurnId: null, |
| 416 | lastError: null, |
| 417 | updatedAt: "2026-05-25T00:00:02.000Z", |
| 418 | }, |
| 419 | }, |
| 377 | 420 | { |
| 378 | 421 | ...baseThread, |
| 379 | 422 | id: "thread-missing-project" as ThreadId, |
| ... | ... | @@ -389,7 +432,7 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { |
| 389 | 432 | }, |
| 390 | 433 | ], |
| 391 | 434 | }), |
| 392 | | ).toEqual([activeThreadId]); |
| 435 | ).toEqual([activeThreadId, newCompletedId]); |
| 393 | 436 | }); |
| 394 | 437 | |
| 395 | 438 | it.effect("signs the activity publish JWT and rejects tampering", () => |
| ... | ... | @@ -797,4 +840,138 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { |
| 797 | 840 | }), |
| 798 | 841 | ), |
| 799 | 842 | ); |
| 843 | |
| 844 | it.effect("does not alert for historical completions after startup", () => |
| 845 | Effect.scoped( |
| 846 | Effect.gen(function* () { |
| 847 | const secrets = makeMemorySecretStore(); |
| 848 | const now = yield* DateTime.now; |
| 849 | const old = DateTime.formatIso(DateTime.add(now, { days: -7 })); |
| 850 | const threadId = "thread-old" as ThreadId; |
| 851 | const projectId = "project-1" as ProjectId; |
| 852 | const environmentId = "env-1" as EnvironmentId; |
| 853 | const project = { |
| 854 | id: projectId, |
| 855 | title: "T3 Code", |
| 856 | workspaceRoot: "/workspace", |
| 857 | repositoryIdentity: null, |
| 858 | defaultModelSelection: null, |
| 859 | scripts: [], |
| 860 | createdAt: old, |
| 861 | updatedAt: old, |
| 862 | } satisfies OrchestrationProjectShell; |
| 863 | const completedTurn = { |
| 864 | turnId: "turn-1" as TurnId, |
| 865 | state: "completed", |
| 866 | requestedAt: old, |
| 867 | startedAt: old, |
| 868 | completedAt: old, |
| 869 | assistantMessageId: null, |
| 870 | } as const; |
| 871 | const completedThread = { |
| 872 | id: threadId, |
| 873 | projectId, |
| 874 | title: "Old task", |
| 875 | modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4" }, |
| 876 | runtimeMode: "full-access", |
| 877 | interactionMode: "default", |
| 878 | branch: null, |
| 879 | worktreePath: null, |
| 880 | pullRequests: [], |
| 881 | latestTurn: completedTurn, |
| 882 | createdAt: old, |
| 883 | updatedAt: old, |
| 884 | archivedAt: null, |
| 885 | settledOverride: null, |
| 886 | settledAt: null, |
| 887 | session: null, |
| 888 | latestUserMessageAt: old, |
| 889 | hasPendingApprovals: false, |
| 890 | hasPendingUserInput: false, |
| 891 | hasActionableProposedPlan: false, |
| 892 | } satisfies OrchestrationThreadShell; |
| 893 | let currentThread: OrchestrationThreadShell | null = completedThread; |
| 894 | let publishes = 0; |
| 895 | const originalFetch = globalThis.fetch; |
| 896 | globalThis.fetch = (() => { |
| 897 | publishes += 1; |
| 898 | return Promise.resolve(Response.json({ ok: true, deliveries: [] })); |
| 899 | }) as unknown as typeof fetch; |
| 900 | yield* Effect.addFinalizer(() => |
| 901 | Effect.sync(() => { |
| 902 | globalThis.fetch = originalFetch; |
| 903 | }), |
| 904 | ); |
| 905 | yield* secrets.setString(RELAY_URL_SECRET, "https://relay.example.test"); |
| 906 | yield* secrets.setString(RELAY_ENVIRONMENT_CREDENTIAL_SECRET, "relay-credential"); |
| 907 | yield* secrets.setString(PUBLISH_AGENT_ACTIVITY_SECRET, "true"); |
| 908 | |
| 909 | const layer = Layer.mergeAll( |
| 910 | Layer.succeed(ServerSecretStore.ServerSecretStore, secrets.store), |
| 911 | Layer.succeed(ServerEnvironment.ServerEnvironment, { |
| 912 | getEnvironmentId: Effect.succeed(environmentId), |
| 913 | getDescriptor: Effect.die("unused descriptor"), |
| 914 | }), |
| 915 | Layer.succeed(OrchestrationEngineService, {} as OrchestrationEngineShape), |
| 916 | Layer.succeed(ProjectionSnapshotQuery, { |
| 917 | getThreadShellById: () => Effect.sync(() => Option.fromNullishOr(currentThread)), |
| 918 | getProjectShellById: () => Effect.succeed(Option.some(project)), |
| 919 | } as unknown as ProjectionSnapshotQueryShape), |
| 920 | ); |
| 921 | |
| 922 | yield* Effect.gen(function* () { |
| 923 | const relay = yield* AgentAwarenessRelay.AgentAwarenessRelay; |
| 924 | yield* relay.publishThread(threadId); |
| 925 | expect(publishes).toBe(0); |
| 926 | |
| 927 | currentThread = { |
| 928 | ...completedThread, |
| 929 | latestTurn: null, |
| 930 | latestUserMessageAt: DateTime.formatIso(DateTime.add(now, { seconds: 1 })), |
| 931 | session: { |
| 932 | threadId, |
| 933 | status: "ready", |
| 934 | providerName: "Codex", |
| 935 | runtimeMode: "full-access", |
| 936 | activeTurnId: null, |
| 937 | lastError: null, |
| 938 | updatedAt: DateTime.formatIso(DateTime.add(now, { seconds: 2 })), |
| 939 | }, |
| 940 | }; |
| 941 | expect( |
| 942 | AgentAwarenessRelay.resolveAgentAwarenessRelayPublishSnapshot({ |
| 943 | environmentId, |
| 944 | threadId, |
| 945 | thread: Option.some(currentThread), |
| 946 | project: Option.some(project), |
| 947 | }).state?.phase, |
| 948 | ).toBe("completed"); |
| 949 | yield* relay.publishThread(threadId); |
| 950 | expect(publishes).toBe(0); |
| 951 | |
| 952 | currentThread = { |
| 953 | ...completedThread, |
| 954 | session: { |
| 955 | threadId, |
| 956 | status: "error", |
| 957 | providerName: "Codex", |
| 958 | runtimeMode: "full-access", |
| 959 | activeTurnId: null, |
| 960 | lastError: "old failure", |
| 961 | updatedAt: old, |
| 962 | }, |
| 963 | }; |
| 964 | yield* relay.publishThread(threadId); |
| 965 | expect(publishes).toBe(0); |
| 966 | }).pipe( |
| 967 | Effect.provide( |
| 968 | AgentAwarenessRelay.layer.pipe( |
| 969 | Layer.provide(layer), |
| 970 | Layer.provideMerge(NodeServices.layer), |
| 971 | ), |
| 972 | ), |
| 973 | ); |
| 974 | }), |
| 975 | ), |
| 976 | ); |
| 800 | 977 | }); |