Gateway: T3 RPC client, host connection, awareness port, storage, API contract
This commit is contained in:
1 parent
0fd556e313
commit
972f6e4be5
13 files changed
+1332
-9
No files matched your search
@@ -0,0 +1,76 @@
|
||||
// What a thread is doing, from the watch's point of view. Ported from T3 Code's
|
||||
// `packages/shared/src/agentAwareness.ts` (MIT), which drives T3's own mobile notifications.
|
||||
|
||||
import type { ThreadShell } from "./t3/types.ts";
|
||||
|
||||
export type Phase =
|
||||
| "starting"
|
||||
| "running"
|
||||
| "waiting_for_approval"
|
||||
| "waiting_for_input"
|
||||
| "completed"
|
||||
| "failed";
|
||||
|
||||
type AwarenessFields = Pick<
|
||||
ThreadShell,
|
||||
| "activityRunStatus"
|
||||
| "lineage"
|
||||
| "latestRunStopRequested"
|
||||
| "pendingBackgroundTasks"
|
||||
| "pendingRuntimeRequest"
|
||||
| "settledOverride"
|
||||
| "status"
|
||||
>;
|
||||
|
||||
/**
|
||||
* The phase to show for a thread, or null when the watch should not show it: subagent children,
|
||||
* threads that never ran or were stopped, and finished work the user already settled.
|
||||
*/
|
||||
export function threadPhase(thread: AwarenessFields): Phase | null {
|
||||
if (thread.lineage.relationshipToParent === "subagent") return null;
|
||||
const phase = resolvePhase(thread);
|
||||
if (phase === null) return null;
|
||||
if (phase === "completed" && thread.latestRunStopRequested === true) return null;
|
||||
if (
|
||||
thread.settledOverride === "settled" &&
|
||||
(phase === "completed" || phase === "failed") &&
|
||||
!backgroundWorkHoldsCompletion(thread.pendingBackgroundTasks ?? [])
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return phase;
|
||||
}
|
||||
|
||||
function resolvePhase(thread: AwarenessFields): Phase | null {
|
||||
const runStatus = thread.activityRunStatus ?? thread.status;
|
||||
const request = thread.pendingRuntimeRequest;
|
||||
if (
|
||||
request?.kind === "user_input" &&
|
||||
(request.blocking !== false || !(runStatus === "preparing" || runStatus === "starting" || runStatus === "running"))
|
||||
) {
|
||||
return "waiting_for_input";
|
||||
}
|
||||
if (request && request.kind !== "user_input" && request.kind !== "auth_refresh") {
|
||||
return "waiting_for_approval";
|
||||
}
|
||||
switch (runStatus) {
|
||||
case "preparing":
|
||||
case "starting":
|
||||
return "starting";
|
||||
case "running":
|
||||
case "waiting":
|
||||
return "running";
|
||||
case "completed":
|
||||
return backgroundWorkHoldsCompletion(thread.pendingBackgroundTasks ?? []) ? "running" : "completed";
|
||||
case "failed":
|
||||
return "failed";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Commands and monitors are as often a dev server or its watchdog as a wait, so they hold
|
||||
// completion only when the agent marks them awaited.
|
||||
function backgroundWorkHoldsCompletion(tasks: NonNullable<ThreadShell["pendingBackgroundTasks"]>): boolean {
|
||||
return tasks.some((task) => task.awaited === true || task.kind === "subagent" || task.kind === "background_task");
|
||||
}
|
||||
Reference in new issue
Block a user