feat(agent): support Responses steering and project-aware tools
Use the dedicated TJWATER_AGENT_URL and Responses stream protocol. Keep running turns active while users steer or queue follow-ups and edit pending delivery. Refresh the model catalog before generation and preserve approval mode selection. Integrate scoped investigation messaging/presentation, geographic place selection, current Agent composer styles and supporting workbench contracts. Validation: 77 focused Vitest checks, 21 browser cases, lint, typecheck and production build. Preserve direct Server/Agent routing and project authentication.
This commit is contained in:
@@ -94,6 +94,7 @@ type ScheduledConditionFeedProps = {
|
||||
onLoadTaskEvidenceSnapshot?: (taskId: string, evidenceId: string) => Promise<OperationalTaskDataSnapshot>;
|
||||
onTaskRefresh?: () => void;
|
||||
onLoadTaskReports?: (taskId: string, offset: number) => Promise<OperationalTaskReportPage>;
|
||||
onLocate?: (targets: import("../api/investigation-report").InvestigationMapTarget[]) => void;
|
||||
onReportViewed?: (taskId: string, generation: number, revision: number) => void;
|
||||
onRefresh?: () => void | Promise<void>;
|
||||
};
|
||||
@@ -133,6 +134,7 @@ export function ScheduledConditionFeed({
|
||||
onLoadTaskEvidenceSnapshot,
|
||||
onTaskRefresh,
|
||||
onLoadTaskReports,
|
||||
onLocate,
|
||||
onReportViewed,
|
||||
onRefresh
|
||||
}: ScheduledConditionFeedProps) {
|
||||
@@ -140,10 +142,14 @@ export function ScheduledConditionFeed({
|
||||
const [uncontrolledSelectedConditionId, setUncontrolledSelectedConditionId] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [retainedScheduledCondition, setRetainedScheduledCondition] = useState<ScheduledConditionItem | null>(null);
|
||||
const [taskFilter, setTaskFilter] = useState<ConditionTaskFilterValue>(CONDITION_FILTER_ALL);
|
||||
const [statusFilter, setStatusFilter] =
|
||||
useState<ConditionStatusFilterValue>(CONDITION_FILTER_ALL);
|
||||
const [taskCategory, setTaskCategory] = useState<WorkbenchTaskCategory>("scheduled");
|
||||
const [taskCategory, setTaskCategory] = useState<WorkbenchTaskCategory>(() => {
|
||||
const selected = conditions.find((condition) => condition.id === controlledSelectedConditionId);
|
||||
return selected ? getWorkbenchTaskCategory(selected) ?? "scheduled" : "scheduled";
|
||||
});
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const refreshingRef = useRef(false);
|
||||
const mobileSheet = presentation === "mobile-sheet";
|
||||
@@ -156,7 +162,14 @@ export function ScheduledConditionFeed({
|
||||
() => groupConditionsByTaskCategory(conditions),
|
||||
[conditions]
|
||||
);
|
||||
const categoryConditions = conditionsByCategory[taskCategory];
|
||||
const categoryConditions = useMemo(() => {
|
||||
const current = conditionsByCategory[taskCategory];
|
||||
return taskCategory === "scheduled"
|
||||
&& retainedScheduledCondition?.id === selectedConditionId
|
||||
&& !current.some((condition) => condition.id === selectedConditionId)
|
||||
? [...current, retainedScheduledCondition]
|
||||
: current;
|
||||
}, [conditionsByCategory, retainedScheduledCondition, selectedConditionId, taskCategory]);
|
||||
const activeLoading = taskCategory === "scheduled" ? loading : backendLoading;
|
||||
const { recentConditions: allRecentConditions, futureWorkOrders: allFutureWorkOrders } = useMemo(
|
||||
() => groupScheduledConditions(categoryConditions),
|
||||
@@ -244,11 +257,18 @@ export function ScheduledConditionFeed({
|
||||
return;
|
||||
}
|
||||
|
||||
// A controlled task selection survives panel remounts and detail-loading windows.
|
||||
const selected = conditions.find((condition) => condition.id === controlledSelectedConditionId);
|
||||
if (selected) {
|
||||
const category = getWorkbenchTaskCategory(selected);
|
||||
if (category && category !== taskCategory) { setTaskCategory(category); return; }
|
||||
} else if (controlledSelectedConditionId && (loading || backendLoading)) return;
|
||||
|
||||
const nextSelectedConditionId = selectedCondition?.id ?? null;
|
||||
if (selectedConditionId !== nextSelectedConditionId) {
|
||||
updateSelectedConditionId(nextSelectedConditionId);
|
||||
}
|
||||
}, [expanded, selectedCondition?.id, selectedConditionId, updateSelectedConditionId]);
|
||||
}, [backendLoading, conditions, controlledSelectedConditionId, expanded, loading, selectedCondition?.id, selectedConditionId, taskCategory, updateSelectedConditionId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!focusConditionId || focusRequestId === undefined) {
|
||||
@@ -268,6 +288,7 @@ export function ScheduledConditionFeed({
|
||||
setTaskCategory(targetCategory);
|
||||
setTaskFilter(CONDITION_FILTER_ALL);
|
||||
setStatusFilter(CONDITION_FILTER_ALL);
|
||||
setRetainedScheduledCondition(targetCategory === "scheduled" ? targetCondition : null);
|
||||
updateSelectedConditionId(targetCondition.id);
|
||||
setFocusedAction(focusRequest?.actionId ? { conditionId: targetCondition.id, actionId: focusRequest.actionId } : null);
|
||||
updateExpanded(true);
|
||||
@@ -293,6 +314,7 @@ export function ScheduledConditionFeed({
|
||||
setTaskCategory(nextCategory);
|
||||
setTaskFilter(CONDITION_FILTER_ALL);
|
||||
setStatusFilter(CONDITION_FILTER_ALL);
|
||||
setRetainedScheduledCondition(null);
|
||||
updateSelectedConditionId(
|
||||
sortConditionsNewestFirst(conditionsByCategory[nextCategory])[0]?.id ?? null
|
||||
);
|
||||
@@ -329,6 +351,11 @@ export function ScheduledConditionFeed({
|
||||
}
|
||||
|
||||
function handleSelectTimelineCondition(conditionId: string) {
|
||||
if (taskCategory === "scheduled") {
|
||||
setRetainedScheduledCondition(
|
||||
categoryConditions.find((condition) => condition.id === conditionId) ?? null
|
||||
);
|
||||
}
|
||||
updateSelectedConditionId(conditionId);
|
||||
if (!expanded) {
|
||||
updateExpanded(true);
|
||||
@@ -521,6 +548,7 @@ export function ScheduledConditionFeed({
|
||||
<ConditionDetailSkeleton />
|
||||
) : expanded && selectedCondition ? (
|
||||
<ConditionDetailPanel
|
||||
onLocate={onLocate}
|
||||
condition={selectedCondition}
|
||||
dataSource={dataSource}
|
||||
workOrderDataSource={workOrderDataSource}
|
||||
|
||||
Reference in New Issue
Block a user