Skip to content
Source
Sign inSign up
Open History to see the last commit

agentAwareness.test.ts

268 lines7.7 KB

1import { describe, expect, it } from "@effect/vitest";
2​
3import type {
4 EnvironmentId,
5 OrchestrationV2ThreadShell,
6 Project,
7 ThreadId,
8} from "@t3tools/contracts";
9import { ProviderInstanceId, RuntimeRequestId } from "@t3tools/contracts";
10import * as DateTime from "effect/DateTime";
11​
12import { projectThreadAwarenessV2 } from "./agentAwareness.ts";
13​
14const NOW = "2026-05-22T12:00:00.000Z";
15​
16const project = {
17 title: "t3code",
18} satisfies Pick<Project, "title">;
19​
20describe("projectThreadAwarenessV2", () => {
21 const updatedAt = DateTime.makeUnsafe(NOW);
22 const v2Thread = (
23 overrides: Partial<
24 Pick<
25 OrchestrationV2ThreadShell,
26 | "activityRunStatus"
27 | "latestRunBackgroundWake"
28 | "latestRunStopRequested"
29 | "status"
30 | "pendingBackgroundTasks"
31 | "pendingRuntimeRequest"
32 | "lineage"
33 | "settledOverride"
34 >
35 > = {},
36 ) => ({
37 id: "thread-2" as ThreadId,
38 lineage: {
39 rootThreadId: "thread-2" as ThreadId,
40 parentThreadId: null,
41 relationshipToParent: null,
42 },
43 title: "Integrate orchestration",
44 modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4" },
45 status: "running" as const,
46 pendingRuntimeRequest: null,
47 settledOverride: null,
48 updatedAt,
49 ...overrides,
50 });
51​
52 it("does not announce completion after a manual stop", () => {
53 const input = {
54 environmentId: "env-1" as EnvironmentId,
55 project,
56 thread: v2Thread({ status: "completed" }),
57 };
58 expect(projectThreadAwarenessV2(input)?.phase).toBe("completed");
59 expect(
60 projectThreadAwarenessV2({
61 ...input,
62 thread: { ...input.thread, latestRunStopRequested: true },
63 }),
64 ).toBeNull();
65 });
66​
67 it.each([
68 ["holds a wake run", true, [{ taskId: "watch", kind: "monitor" as const }], "running"],
69 ["completes a wake run once its monitor ends", true, [], "completed"],
70 [
71 "holds a wake run while a monitor remains beside other work",
72 true,
73 [
74 { taskId: "build", kind: "command" as const },
75 { taskId: "watch", kind: "monitor" as const },
76 ],
77 "running",
78 ],
79 [
80 "completes a wake run with only a command pending",
81 true,
82 [{ taskId: "build", kind: "command" as const }],
83 "completed",
84 ],
85 [
86 "completes a run the user started",
87 false,
88 [{ taskId: "watch", kind: "monitor" as const }],
89 "completed",
90 ],
91 ] as const)("%s while a monitor wakes the thread", (_, latestRunBackgroundWake, tasks, phase) => {
92 expect(
93 projectThreadAwarenessV2({
94 environmentId: "env-1" as EnvironmentId,
95 project,
96 thread: v2Thread({
97 status: "completed",
98 latestRunBackgroundWake,
99 pendingBackgroundTasks: [...tasks],
100 }),
101 })?.phase,
102 ).toBe(phase);
103 });
104​
105 it.each([
106 ["running", "subagent"],
107 ["completed", "subagent"],
108 ["failed", "subagent"],
109 ["running", "side chat"],
110 ] as const)("does not publish %s %s activity", (status, kind) => {
111 expect(
112 projectThreadAwarenessV2({
113 environmentId: "env-1" as EnvironmentId,
114 project,
115 thread: v2Thread({
116 status,
117 lineage: {
118 rootThreadId: "parent" as ThreadId,
119 parentThreadId: "parent" as ThreadId,
120 ...(kind === "subagent"
121 ? { relationshipToParent: "subagent" }
122 : { relationshipToParent: "fork", sideChat: true }),
123 },
124 }),
125 }),
126 ).toBeNull();
127 });
128​
129 it("keeps an older activity run visible over a newer cancelled run", () => {
130 expect(
131 projectThreadAwarenessV2({
132 environmentId: "env-1" as EnvironmentId,
133 project,
134 thread: v2Thread({ status: "cancelled", activityRunStatus: "running" }),
135 }),
136 ).toMatchObject({ phase: "running", headline: "Agent is working" });
137 });
138​
139 it.each([
140 ["completed", "completed", false],
141 ["failed", "failed", false],
142 ["monitor-held", "completed", true],
143 ] as const)("removes settled %s work", (_, status, latestRunBackgroundWake) => {
144 expect(
145 projectThreadAwarenessV2({
146 environmentId: "env-1" as EnvironmentId,
147 project,
148 thread: v2Thread({
149 status,
150 settledOverride: "settled",
151 latestRunBackgroundWake,
152 pendingBackgroundTasks: [{ taskId: "watch", kind: "monitor" }],
153 }),
154 }),
155 ).toBeNull();
156 });
157​
158 it.each(["starting", "running", "waiting"] as const)(
159 "keeps settled %s work visible",
160 (status) => {
161 expect(
162 projectThreadAwarenessV2({
163 environmentId: "env-1" as EnvironmentId,
164 project,
165 thread: v2Thread({ status, settledOverride: "settled" }),
166 }),
167 ).toMatchObject({ phase: status === "starting" ? "starting" : "running" });
168 },
169 );
170​
171 it.each(["user_input", "command"] as const)(
172 "keeps a settled terminal thread with a pending %s request",
173 (kind) => {
174 expect(
175 projectThreadAwarenessV2({
176 environmentId: "env-1" as EnvironmentId,
177 project,
178 thread: v2Thread({
179 status: "completed",
180 settledOverride: "settled",
181 pendingRuntimeRequest: {
182 id: RuntimeRequestId.make("request-settled"),
183 kind,
184 createdAt: updatedAt,
185 },
186 }),
187 }),
188 ).toMatchObject({
189 phase: kind === "user_input" ? "waiting_for_input" : "waiting_for_approval",
190 });
191 },
192 );
193​
194 it("keeps settled work held open by an awaited monitor", () => {
195 expect(
196 projectThreadAwarenessV2({
197 environmentId: "env-1" as EnvironmentId,
198 project,
199 thread: v2Thread({
200 status: "completed",
201 settledOverride: "settled",
202 pendingBackgroundTasks: [{ taskId: "watch", kind: "monitor", awaited: true }],
203 }),
204 }),
205 ).toMatchObject({ phase: "running" });
206 });
207​
208 it.each([
209 ["only a dev server", "completed", [{ taskId: "dev", kind: "command" }]],
210 ["a dev server's watchdog monitor", "completed", [{ taskId: "watch", kind: "monitor" }]],
211 [
212 "a command the agent waits on",
213 "running",
214 [{ taskId: "tests", kind: "command", awaited: true }],
215 ],
216 [
217 "a dev server and a subagent",
218 "running",
219 [
220 { taskId: "dev", kind: "command" },
221 { taskId: "review", kind: "subagent" },
222 ],
223 ],
224 ] as const)("reports a completed run waiting on %s as %s", (_case, phase, tasks) => {
225 expect(
226 projectThreadAwarenessV2({
227 environmentId: "env-1" as EnvironmentId,
228 project,
229 thread: v2Thread({ status: "completed", pendingBackgroundTasks: tasks }),
230 }),
231 ).toMatchObject({ phase });
232 });
233​
234 it("reports an async question as input while the run keeps working", () => {
235 const input = {
236 environmentId: "env-1" as EnvironmentId,
237 project,
238 thread: v2Thread({
239 pendingRuntimeRequest: {
240 id: RuntimeRequestId.make("request-async"),
241 kind: "user_input",
242 createdAt: updatedAt,
243 blocking: false,
244 },
245 }),
246 };
247 expect(projectThreadAwarenessV2(input)).toMatchObject({
248 phase: "waiting_for_input",
249 headline: "Waiting for input",
250 });
251 });
252​
253 it("does not present authentication refreshes as user approvals", () => {
254 expect(
255 projectThreadAwarenessV2({
256 environmentId: "env-1" as EnvironmentId,
257 project,
258 thread: v2Thread({
259 pendingRuntimeRequest: {
260 id: RuntimeRequestId.make("request-auth-refresh"),
261 kind: "auth_refresh",
262 createdAt: updatedAt,
263 },
264 }),
265 }),
266 ).toMatchObject({ phase: "running", headline: "Agent is working" });
267 });
268});