Fix repo typecheck failures
This commit is contained in:
@@ -856,7 +856,7 @@ const stylesheet = StyleSheet.create((theme) => ({
|
||||
workingIndicatorText: {
|
||||
color: theme.colors.mutedForeground,
|
||||
fontSize: theme.fontSize.sm,
|
||||
fontWeight: theme.fontWeight.medium,
|
||||
fontWeight: theme.fontWeight.semibold,
|
||||
},
|
||||
workingDotsRow: {
|
||||
flexDirection: "row",
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { hydrateStreamState, type StreamItem, type ToolCallItem } from "./stream";
|
||||
import {
|
||||
hydrateStreamState,
|
||||
type StreamItem,
|
||||
type AgentToolCallItem,
|
||||
type ToolCallStatus,
|
||||
isAgentToolCallItem,
|
||||
} from "./stream";
|
||||
import type { AgentStreamEventPayload } from "@server/server/messages";
|
||||
|
||||
type HarnessUpdate = { event: AgentStreamEventPayload; timestamp: Date };
|
||||
@@ -200,14 +206,14 @@ function buildToolStartEvent({
|
||||
tool,
|
||||
input,
|
||||
kind,
|
||||
status = "pending",
|
||||
status = "executing",
|
||||
}: {
|
||||
callId: string;
|
||||
server: string;
|
||||
tool: string;
|
||||
input?: Record<string, unknown>;
|
||||
kind?: string;
|
||||
status?: string;
|
||||
status?: ToolCallStatus;
|
||||
}): AgentStreamEventPayload {
|
||||
return {
|
||||
type: "timeline",
|
||||
@@ -260,14 +266,15 @@ function buildToolResultEvent({
|
||||
};
|
||||
}
|
||||
|
||||
function extractHarnessSnapshots(state: StreamItem[]): Record<keyof typeof HARNESS_CALL_IDS, ToolCallItem | undefined> {
|
||||
const lookup = Object.values(HARNESS_CALL_IDS).reduce<Record<string, ToolCallItem | undefined>>(
|
||||
(acc, id) => {
|
||||
acc[id] = findToolByCallId(state, id);
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
function extractHarnessSnapshots(
|
||||
state: StreamItem[]
|
||||
): Record<keyof typeof HARNESS_CALL_IDS, AgentToolCallItem | undefined> {
|
||||
const lookup = Object.values(HARNESS_CALL_IDS).reduce<
|
||||
Record<string, AgentToolCallItem | undefined>
|
||||
>((acc, id) => {
|
||||
acc[id] = findToolByCallId(state, id);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return {
|
||||
command: lookup[HARNESS_CALL_IDS.command],
|
||||
@@ -276,11 +283,9 @@ function extractHarnessSnapshots(state: StreamItem[]): Record<keyof typeof HARNE
|
||||
};
|
||||
}
|
||||
|
||||
function findToolByCallId(state: StreamItem[], callId: string): ToolCallItem | undefined {
|
||||
function findToolByCallId(state: StreamItem[], callId: string): AgentToolCallItem | undefined {
|
||||
return state.find(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === "tool_call" &&
|
||||
item.payload.source === "agent" &&
|
||||
item.payload.data.callId === callId
|
||||
(item): item is AgentToolCallItem =>
|
||||
isAgentToolCallItem(item) && item.payload.data.callId === callId
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,11 +7,21 @@ import {
|
||||
type StreamItem,
|
||||
type ToolCallItem,
|
||||
type TodoListItem,
|
||||
type AgentToolCallItem,
|
||||
isAgentToolCallItem,
|
||||
} from "./stream";
|
||||
|
||||
type AgentStreamEventPayload = Parameters<typeof reduceStreamUpdate>[1];
|
||||
type TestAgentProvider = "claude" | "codex";
|
||||
|
||||
function expectAgentToolCallItem(
|
||||
item: ToolCallItem | undefined,
|
||||
message = "Expected agent tool call"
|
||||
): asserts item is AgentToolCallItem {
|
||||
assert.ok(item, message);
|
||||
assert.ok(isAgentToolCallItem(item), message);
|
||||
}
|
||||
|
||||
function assistantTimeline(text: string, provider: TestAgentProvider = "claude"): AgentStreamEventPayload {
|
||||
return {
|
||||
type: "timeline",
|
||||
@@ -157,7 +167,7 @@ function testUserMessageDeduplication() {
|
||||
|
||||
const state = hydrateStreamState(updates);
|
||||
|
||||
const toolCalls = state.filter((item) => item.kind === "tool_call");
|
||||
const toolCalls = state.filter(isAgentToolCallItem);
|
||||
|
||||
assert.strictEqual(toolCalls.length, 1, "Pending/completed tool entries should reconcile");
|
||||
assert.strictEqual(toolCalls[0].payload.source, "agent");
|
||||
@@ -212,13 +222,12 @@ function testToolCallInputPreservation() {
|
||||
];
|
||||
|
||||
const state = hydrateStreamState(updates);
|
||||
const toolCallEntry = state.find(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === "tool_call" && item.payload.source === "agent"
|
||||
const toolCallEntry = state.find((item): item is AgentToolCallItem =>
|
||||
isAgentToolCallItem(item)
|
||||
);
|
||||
|
||||
assert.ok(toolCallEntry, "Tool call entry expected after hydration");
|
||||
const rawPayload = toolCallEntry!.payload.data.raw as unknown;
|
||||
expectAgentToolCallItem(toolCallEntry, "Tool call entry expected after hydration");
|
||||
const rawPayload = toolCallEntry.payload.data.raw as unknown;
|
||||
|
||||
assert.ok(Array.isArray(rawPayload), "Raw payload should contain both input and result entries");
|
||||
assert.strictEqual(rawPayload[0], toolInput);
|
||||
@@ -263,8 +272,8 @@ function testToolCallStatusInference() {
|
||||
]);
|
||||
|
||||
const toolEntry = state.find(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === 'tool_call' && item.payload.source === 'agent' && item.payload.data.callId === toolCallId
|
||||
(item): item is AgentToolCallItem =>
|
||||
isAgentToolCallItem(item) && item.payload.data.callId === toolCallId
|
||||
);
|
||||
|
||||
assert.ok(toolEntry, "Tool entry should exist after hydration");
|
||||
@@ -296,10 +305,7 @@ function testToolCallStatusInferenceFromRawOnly() {
|
||||
};
|
||||
|
||||
const state = hydrateStreamState([{ event: rawEvent, timestamp }]);
|
||||
const toolEntry = state.find(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === 'tool_call' && item.payload.source === 'agent'
|
||||
);
|
||||
const toolEntry = state.find(isAgentToolCallItem);
|
||||
|
||||
assert.strictEqual(toolEntry?.payload.data.status, 'completed');
|
||||
}
|
||||
@@ -328,12 +334,9 @@ function testToolCallFailureInferenceFromRaw() {
|
||||
};
|
||||
|
||||
const state = hydrateStreamState([{ event: rawEvent, timestamp }]);
|
||||
const toolEntry = state.find(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === 'tool_call' && item.payload.source === 'agent'
|
||||
);
|
||||
|
||||
assert.strictEqual(toolEntry?.payload.data.status, 'failed');
|
||||
const toolEntry = state.find(isAgentToolCallItem);
|
||||
expectAgentToolCallItem(toolEntry);
|
||||
assert.strictEqual(toolEntry.payload.data.status, 'failed');
|
||||
}
|
||||
|
||||
function testToolCallLateCallIdReconciliation() {
|
||||
@@ -349,10 +352,7 @@ function testToolCallLateCallIdReconciliation() {
|
||||
];
|
||||
|
||||
const state = hydrateStreamState(updates);
|
||||
const toolCalls = state.filter(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === 'tool_call' && item.payload.source === 'agent'
|
||||
);
|
||||
const toolCalls = state.filter(isAgentToolCallItem);
|
||||
|
||||
assert.strictEqual(toolCalls.length, 1, 'late call ids should merge entries');
|
||||
assert.strictEqual(toolCalls[0].payload.data.status, 'completed');
|
||||
@@ -460,16 +460,12 @@ function testToolCallParsedPayloadHydration() {
|
||||
|
||||
const state = hydrateStreamState(updates);
|
||||
const readEntry = state.find(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === 'tool_call' &&
|
||||
item.payload.source === 'agent' &&
|
||||
item.payload.data.callId === readCallId
|
||||
(item): item is AgentToolCallItem =>
|
||||
isAgentToolCallItem(item) && item.payload.data.callId === readCallId
|
||||
);
|
||||
const commandEntry = state.find(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === 'tool_call' &&
|
||||
item.payload.source === 'agent' &&
|
||||
item.payload.data.callId === commandCallId
|
||||
(item): item is AgentToolCallItem =>
|
||||
isAgentToolCallItem(item) && item.payload.data.callId === commandCallId
|
||||
);
|
||||
|
||||
const readPass = Boolean(
|
||||
@@ -711,22 +707,16 @@ function testClaudeHydratedToolBodies() {
|
||||
|
||||
const state = hydrateStreamState(updates);
|
||||
const editEntry = state.find(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === 'tool_call' &&
|
||||
item.payload.source === 'agent' &&
|
||||
item.payload.data.callId === editCallId
|
||||
(item): item is AgentToolCallItem =>
|
||||
isAgentToolCallItem(item) && item.payload.data.callId === editCallId
|
||||
);
|
||||
const readEntry = state.find(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === 'tool_call' &&
|
||||
item.payload.source === 'agent' &&
|
||||
item.payload.data.callId === readCallId
|
||||
(item): item is AgentToolCallItem =>
|
||||
isAgentToolCallItem(item) && item.payload.data.callId === readCallId
|
||||
);
|
||||
const commandEntry = state.find(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === 'tool_call' &&
|
||||
item.payload.source === 'agent' &&
|
||||
item.payload.data.callId === commandCallId
|
||||
(item): item is AgentToolCallItem =>
|
||||
isAgentToolCallItem(item) && item.payload.data.callId === commandCallId
|
||||
);
|
||||
|
||||
const editHasDiff = Boolean(
|
||||
@@ -838,9 +828,7 @@ function testPermissionToolCallFiltering() {
|
||||
];
|
||||
|
||||
const state = hydrateStreamState(updates);
|
||||
const permissionEntries = state.filter(
|
||||
(item) => item.kind === 'tool_call' && item.payload.source === 'agent'
|
||||
);
|
||||
const permissionEntries = state.filter(isAgentToolCallItem);
|
||||
|
||||
assert.strictEqual(permissionEntries.length, 0);
|
||||
}
|
||||
@@ -1030,7 +1018,7 @@ function buildConcurrentToolCallUpdates(provider: ToolCallProvider) {
|
||||
function validateToolCallDeduplication(
|
||||
updates: Array<{ event: AgentStreamEventPayload; timestamp: Date }>,
|
||||
mode: 'live' | 'hydrated'
|
||||
): ToolCallItem[] {
|
||||
): AgentToolCallItem[] {
|
||||
const finalState =
|
||||
mode === 'live'
|
||||
? updates.reduce<StreamItem[]>((state, { event, timestamp }) => {
|
||||
@@ -1038,10 +1026,7 @@ function validateToolCallDeduplication(
|
||||
}, [])
|
||||
: hydrateStreamState(updates);
|
||||
|
||||
return finalState.filter(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === 'tool_call' && item.payload.source === 'agent'
|
||||
);
|
||||
return finalState.filter(isAgentToolCallItem);
|
||||
}
|
||||
|
||||
function testToolCallDeduplicationLive() {
|
||||
@@ -1116,9 +1101,7 @@ function testOutOfOrderToolCallMergingLive() {
|
||||
const finalState = updates.reduce<StreamItem[]>((state, { event, timestamp }) => {
|
||||
return reduceStreamUpdate(state, event, timestamp);
|
||||
}, []);
|
||||
const toolCalls = finalState.filter(
|
||||
(item): item is ToolCallItem => item.kind === 'tool_call' && item.payload.source === 'agent'
|
||||
);
|
||||
const toolCalls = finalState.filter(isAgentToolCallItem);
|
||||
assert.strictEqual(toolCalls.length, 1, `${provider} live stream should not duplicate out-of-order calls`);
|
||||
assert.strictEqual(toolCalls[0]?.payload.data.status, 'completed', `${provider} live stream should keep completed status`);
|
||||
});
|
||||
@@ -1128,9 +1111,7 @@ function testOutOfOrderToolCallMergingHydrated() {
|
||||
(['claude', 'codex'] as const).forEach((provider) => {
|
||||
const updates = buildOutOfOrderToolCallSequence(provider);
|
||||
const hydrated = hydrateStreamState(updates);
|
||||
const toolCalls = hydrated.filter(
|
||||
(item): item is ToolCallItem => item.kind === 'tool_call' && item.payload.source === 'agent'
|
||||
);
|
||||
const toolCalls = hydrated.filter(isAgentToolCallItem);
|
||||
assert.strictEqual(toolCalls.length, 1, `${provider} hydration should not duplicate out-of-order calls`);
|
||||
assert.strictEqual(toolCalls[0]?.payload.data.status, 'completed', `${provider} hydration should keep completed status`);
|
||||
});
|
||||
@@ -1178,7 +1159,7 @@ function buildMetadataReplaySequence(provider: ToolCallProvider) {
|
||||
function validateMetadataReplayDeduplication(
|
||||
updates: Array<{ event: AgentStreamEventPayload; timestamp: Date }>,
|
||||
mode: 'live' | 'hydrated'
|
||||
) {
|
||||
): AgentToolCallItem[] {
|
||||
const finalState =
|
||||
mode === 'live'
|
||||
? updates.reduce<StreamItem[]>((state, { event, timestamp }) => {
|
||||
@@ -1186,11 +1167,7 @@ function validateMetadataReplayDeduplication(
|
||||
}, [])
|
||||
: hydrateStreamState(updates);
|
||||
|
||||
const toolCalls = finalState.filter(
|
||||
(item): item is ToolCallItem => item.kind === 'tool_call' && item.payload.source === 'agent'
|
||||
);
|
||||
|
||||
return toolCalls;
|
||||
return finalState.filter(isAgentToolCallItem);
|
||||
}
|
||||
|
||||
function testMetadataReplayDeduplicationLive() {
|
||||
@@ -1241,9 +1218,7 @@ function testFallbackToolCallIdsStayUnique() {
|
||||
];
|
||||
|
||||
const hydrated = hydrateStreamState(updates);
|
||||
const toolCalls = hydrated.filter(
|
||||
(item): item is ToolCallItem => item.kind === 'tool_call' && item.payload.source === 'agent'
|
||||
);
|
||||
const toolCalls = hydrated.filter(isAgentToolCallItem);
|
||||
|
||||
assert.strictEqual(toolCalls.length, 2, 'Hydration should retain multiple fallback tool entries');
|
||||
const ids = toolCalls.map((item) => item.id);
|
||||
@@ -1284,10 +1259,8 @@ function testCodexProviderEventsProduceToolCalls() {
|
||||
}, []);
|
||||
|
||||
const commandTool = commandState.find(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === 'tool_call' &&
|
||||
item.payload.source === 'agent' &&
|
||||
item.payload.data.callId === 'cmd-provider-1'
|
||||
(item): item is AgentToolCallItem =>
|
||||
isAgentToolCallItem(item) && item.payload.data.callId === 'cmd-provider-1'
|
||||
);
|
||||
|
||||
assert.ok(commandTool, 'Codex provider command events should create tool call entries');
|
||||
@@ -1327,10 +1300,8 @@ function testCodexProviderEventsProduceToolCalls() {
|
||||
}, []);
|
||||
|
||||
const mcpTool = mcpState.find(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === 'tool_call' &&
|
||||
item.payload.source === 'agent' &&
|
||||
item.payload.data.callId === 'mcp-provider-1'
|
||||
(item): item is AgentToolCallItem =>
|
||||
isAgentToolCallItem(item) && item.payload.data.callId === 'mcp-provider-1'
|
||||
);
|
||||
|
||||
assert.ok(mcpTool, 'Codex provider MCP events should create tool call entries');
|
||||
|
||||
@@ -90,20 +90,22 @@ export interface ThoughtItem {
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export type ToolCallStatus = "executing" | "completed" | "failed";
|
||||
|
||||
interface OrchestratorToolCallData {
|
||||
toolCallId: string;
|
||||
toolName: string;
|
||||
arguments: unknown;
|
||||
result?: unknown;
|
||||
error?: unknown;
|
||||
status: "executing" | "completed" | "failed";
|
||||
status: ToolCallStatus;
|
||||
}
|
||||
|
||||
export interface AgentToolCallData {
|
||||
provider: AgentProvider;
|
||||
server: string;
|
||||
tool: string;
|
||||
status: string;
|
||||
status?: ToolCallStatus;
|
||||
raw?: unknown;
|
||||
callId?: string;
|
||||
displayName?: string;
|
||||
@@ -126,10 +128,14 @@ export interface ToolCallItem {
|
||||
payload: ToolCallPayload;
|
||||
}
|
||||
|
||||
type AgentToolCallItem = ToolCallItem & {
|
||||
export type AgentToolCallItem = ToolCallItem & {
|
||||
payload: { source: "agent"; data: AgentToolCallData };
|
||||
};
|
||||
|
||||
export function isAgentToolCallItem(item: StreamItem): item is AgentToolCallItem {
|
||||
return item.kind === "tool_call" && item.payload.source === "agent";
|
||||
}
|
||||
|
||||
type ActivityLogType = "system" | "info" | "success" | "error";
|
||||
|
||||
export interface ActivityLogItem {
|
||||
@@ -532,12 +538,6 @@ function findExistingAgentToolCallIndex(
|
||||
return matches.length > 0 ? matches : candidates;
|
||||
};
|
||||
|
||||
const byDisplayName = filterByComparableField(
|
||||
fallbackCandidates,
|
||||
(entry) => normalizeComparableString(entry.payload.data.displayName),
|
||||
normalizedDisplayName
|
||||
);
|
||||
|
||||
const selectCandidate = (
|
||||
candidates: Array<{ index: number; item: AgentToolCallItem }>
|
||||
): number => {
|
||||
@@ -773,7 +773,7 @@ function normalizeToolCallStatus(
|
||||
result?: unknown,
|
||||
error?: unknown,
|
||||
raw?: unknown
|
||||
): "executing" | "completed" | "failed" {
|
||||
): ToolCallStatus {
|
||||
const normalizedFromStatus = normalizeStatusString(status);
|
||||
if (normalizedFromStatus === "failed") {
|
||||
return "failed";
|
||||
@@ -798,9 +798,9 @@ function normalizeToolCallStatus(
|
||||
}
|
||||
|
||||
function mergeToolCallStatus(
|
||||
existing: "executing" | "completed" | "failed" | undefined,
|
||||
incoming: "executing" | "completed" | "failed"
|
||||
): "executing" | "completed" | "failed" {
|
||||
existing: ToolCallStatus | undefined,
|
||||
incoming: ToolCallStatus
|
||||
): ToolCallStatus {
|
||||
if (existing === "failed" || incoming === "failed") {
|
||||
return "failed";
|
||||
}
|
||||
@@ -938,7 +938,7 @@ export function reduceStreamUpdate(
|
||||
provider: event.provider,
|
||||
server: item.server,
|
||||
tool: item.tool,
|
||||
status: item.status ?? "executing",
|
||||
status: normalizeStatusString(item.status) ?? "executing",
|
||||
raw: rawPayload,
|
||||
callId: item.callId,
|
||||
displayName: item.displayName,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"dev:tsx": "NODE_ENV=development tsx watch --ignore '**/*.timestamp-*' src/server/index.ts",
|
||||
"build": "tsc -p tsconfig.server.json",
|
||||
"start": "NODE_ENV=production node dist/server/index.js",
|
||||
"typecheck": "tsc -p tsconfig.server.json --noEmit",
|
||||
"typecheck": "tsc -p tsconfig.server.typecheck.json --noEmit",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
|
||||
@@ -4,7 +4,12 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import { ClaudeAgentClient, convertClaudeHistoryEntry } from "./claude-agent.js";
|
||||
import { hydrateStreamState, type StreamItem, type ToolCallItem, type AgentToolCallData } from "../../../../../app/src/types/stream.js";
|
||||
import {
|
||||
hydrateStreamState,
|
||||
type StreamItem,
|
||||
type AgentToolCallData,
|
||||
isAgentToolCallItem,
|
||||
} from "../../../../../app/src/types/stream.js";
|
||||
import type { AgentStreamEventPayload } from "../../messages.js";
|
||||
import type { AgentProvider, AgentSessionConfig, AgentStreamEvent, AgentTimelineItem } from "../agent-sdk-types.js";
|
||||
|
||||
@@ -596,15 +601,10 @@ function stateIncludesUserMessage(state: StreamItem[], marker: string): boolean
|
||||
}
|
||||
|
||||
function extractAgentToolSnapshots(state: StreamItem[]): ToolSnapshot[] {
|
||||
return state
|
||||
.filter(
|
||||
(item): item is ToolCallItem =>
|
||||
item.kind === "tool_call" && item.payload.source === "agent"
|
||||
)
|
||||
.map((item) => ({
|
||||
key: buildToolSnapshotKey(item.payload.data, item.id),
|
||||
data: item.payload.data,
|
||||
}));
|
||||
return state.filter(isAgentToolCallItem).map((item) => ({
|
||||
key: buildToolSnapshotKey(item.payload.data, item.id),
|
||||
data: item.payload.data,
|
||||
}));
|
||||
}
|
||||
|
||||
function buildToolSnapshotKey(data: AgentToolCallData, fallbackId: string): string {
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"lib": ["ES2020"],
|
||||
"baseUrl": "./src",
|
||||
"paths": {
|
||||
"@server/*": ["./*"]
|
||||
},
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "./tsconfig.server.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "../../"
|
||||
}
|
||||
}
|
||||
@@ -82,3 +82,8 @@
|
||||
- `packages/app/src/components/agent-input-area.tsx` now shows a lone Send control when text/images are queued while keeping Dictate + Realtime visible only when the input is empty or the agent is actively running.
|
||||
- [x] Remove the agent status pill next to the permission selector; instead show a “working” indicator inside the chat scroll view itself (three bouncing dots animation) whenever the agent is busy.
|
||||
- Dropped the old status dot from `AgentStatusBar` (the permission/mode selector now stands alone) and introduced an in-stream "Working" chip inside `AgentStreamView` that renders three bouncing dots via Reanimated whenever the agent reports `status === "running"`, so busy turns surface directly in the chat timeline.
|
||||
- [x] npm run typecheck and fix all problems
|
||||
- Split the server build/typecheck configs so the typechecker can include the app stream helpers without breaking builds, added path aliases for `@server/*`, and tightened the shared stream types (status unions + `isAgentToolCallItem`) so the server e2e suites can import them safely. Cleaned up every failing app/server test by narrowing tool-call payloads via the helper, updated the harness + Codex/Claude specs, and re-ran `npm run typecheck` (app + server) to green.
|
||||
- [ ] audit codebase for unecessary untyped code, hacks, casts, and `any`, type things properly
|
||||
- [ ] audit codebase for duplicated code, clean it up
|
||||
- [ ] rename the project to Paseo, including the Expo app, package names etc.
|
||||
|
||||
Reference in New Issue
Block a user