Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 23 additions & 97 deletions .oxlintrc.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"options": {
"denyWarnings": true,
"typeAware": true,
"reportUnusedDisableDirectives": "off"
"reportUnusedDisableDirectives": "error"
},
"categories": {
"correctness": "error",
Expand Down Expand Up @@ -73,7 +73,12 @@
"hooks/unsupported-syntax": "warn",
"hooks/use-memo": "error",
"import/no-cycle": "error",
"import/no-unassigned-import": "off",
"import/no-unassigned-import": [
"error",
{
"allow": ["**/*.css"]
}
],
"jsx-a11y/alt-text": "error",
"jsx-a11y/anchor-has-content": "error",
"jsx-a11y/anchor-is-valid": "error",
Expand Down Expand Up @@ -211,7 +216,7 @@
"nextjs/no-typos": "warn",
"nextjs/no-unwanted-polyfillio": "warn",
"no-array-constructor": "error",
"no-await-in-loop": "off",
"no-await-in-loop": "error",
"no-case-declarations": "error",
"no-empty": "error",
"no-empty-function": "error",
Expand All @@ -227,11 +232,11 @@
"message": "Read environment variables through an env.ts or env/ module validated with @t3-oss/env-nextjs for Next.js or @t3-oss/env-core for other runtimes."
}
],
"no-shadow": "off",
"no-shadow": "error",
"no-undef": "off",
"no-useless-assignment": "error",
"no-var": "error",
"oxc/no-map-spread": "off",
"oxc/no-map-spread": "error",
"prefer-const": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
Expand All @@ -242,7 +247,7 @@
"react/jsx-no-duplicate-props": "error",
"react/jsx-no-target-blank": "error",
"react/jsx-no-undef": "error",
"react/no-array-index-key": "off",
"react/no-array-index-key": "error",
"react/no-children-prop": "error",
"react/no-danger-with-children": "error",
"react/no-direct-mutation-state": "error",
Expand Down Expand Up @@ -289,7 +294,7 @@
"typescript/class-literal-property-style": "error",
"typescript/consistent-generic-constructors": "error",
"typescript/consistent-indexed-object-style": "error",
"typescript/consistent-return": "off",
"typescript/consistent-return": "error",
"typescript/consistent-type-assertions": "error",
"typescript/consistent-type-definitions": "error",
"typescript/consistent-type-exports": "error",
Expand Down Expand Up @@ -360,8 +365,8 @@
"typescript/return-await": ["error", "error-handling-correctness-only"],
"typescript/unified-signatures": "error",
"typescript/use-unknown-in-catch-callback-variable": "error",
"unicorn/consistent-function-scoping": "off",
"unicorn/no-array-sort": "off",
"unicorn/consistent-function-scoping": "error",
"unicorn/no-array-sort": "error",
"unicorn/no-instanceof-builtins": [
"error",
{
Expand All @@ -370,60 +375,6 @@
]
},
"overrides": [
{
"files": ["**/*.{js,mjs,cjs}"],
"rules": {
"typescript/await-thenable": "off",
"typescript/consistent-type-exports": "off",
"typescript/dot-notation": "off",
"typescript/no-array-delete": "off",
"typescript/no-base-to-string": "off",
"typescript/no-confusing-void-expression": "off",
"typescript/no-deprecated": "off",
"typescript/no-duplicate-type-constituents": "off",
"typescript/no-floating-promises": "off",
"typescript/no-for-in-array": "off",
"typescript/no-implied-eval": "off",
"typescript/no-meaningless-void-operator": "off",
"typescript/no-misused-promises": "off",
"typescript/no-misused-spread": "off",
"typescript/no-mixed-enums": "off",
"typescript/no-redundant-type-constituents": "off",
"typescript/no-unnecessary-boolean-literal-compare": "off",
"typescript/no-unnecessary-condition": "off",
"typescript/no-unnecessary-template-expression": "off",
"typescript/no-unnecessary-type-arguments": "off",
"typescript/no-unnecessary-type-assertion": "off",
"typescript/no-unnecessary-type-conversion": "off",
"typescript/no-unnecessary-type-parameters": "off",
"typescript/no-unsafe-argument": "off",
"typescript/no-unsafe-assignment": "off",
"typescript/no-unsafe-call": "off",
"typescript/no-unsafe-enum-comparison": "off",
"typescript/no-unsafe-member-access": "off",
"typescript/no-unsafe-return": "off",
"typescript/no-unsafe-type-assertion": "off",
"typescript/no-unsafe-unary-minus": "off",
"typescript/no-useless-default-assignment": "off",
"typescript/non-nullable-type-assertion-style": "off",
"typescript/only-throw-error": "off",
"typescript/prefer-find": "off",
"typescript/prefer-includes": "off",
"typescript/prefer-nullish-coalescing": "off",
"typescript/prefer-optional-chain": "off",
"typescript/prefer-promise-reject-errors": "off",
"typescript/prefer-reduce-type-parameter": "off",
"typescript/prefer-regexp-exec": "off",
"typescript/prefer-return-this-type": "off",
"typescript/related-getter-setter-pairs": "off",
"typescript/require-array-sort-compare": "off",
"typescript/restrict-plus-operands": "off",
"typescript/restrict-template-expressions": "off",
"typescript/return-await": "off",
"typescript/unbound-method": "off",
"typescript/use-unknown-in-catch-callback-variable": "off"
}
},
{
"files": [
"**/env.{js,cjs,mjs,ts,cts,mts}",
Expand Down Expand Up @@ -462,41 +413,16 @@
}
},
{
// Eve owns this generated Web Chat scaffold. Keep it updateable from the
// framework.
"files": ["src/app/**/*.tsx", "src/components/ai-elements/**/*.tsx"],
// AI Elements uses typography and animation utilities supplied through
// package styles that the Tailwind linter cannot resolve from the app
// entrypoint. Keep this exception limited to the affected components.
"files": [
"src/components/ai-elements/message.tsx",
"src/components/ai-elements/reasoning.tsx",
"src/components/ai-elements/tool.tsx"
],
"rules": {
"eslint/no-empty-function": "off",
"eslint/no-underscore-dangle": "off",
"hooks/refs": "off",
"hooks/set-state-in-effect": "off",
"hooks/static-components": "off",
"jsx-a11y/anchor-has-content": "off",
"jsx-a11y/control-has-associated-label": "off",
"jsx-a11y/heading-has-content": "off",
"nextjs/no-img-element": "off",
"promise/always-return": "off",
"tailwindcss/enforce-consistent-important-position": "off",
"tailwindcss/enforce-shorthand": "off",
"tailwindcss/no-deprecated-classes": "off",
"tailwindcss/no-unknown-classes": "off",
"typescript/consistent-type-definitions": "off",
"typescript/no-confusing-void-expression": "off",
"typescript/no-deprecated": "off",
"typescript/no-floating-promises": "off",
"typescript/no-misused-promises": "off",
"typescript/no-unnecessary-condition": "off",
"typescript/no-unnecessary-type-assertion": "off",
"typescript/no-unsafe-argument": "off",
"typescript/no-unsafe-assignment": "off",
"typescript/no-unsafe-member-access": "off",
"typescript/no-unsafe-return": "off",
"typescript/no-unsafe-type-assertion": "off",
"typescript/prefer-nullish-coalescing": "off",
"typescript/restrict-template-expressions": "off",
"typescript/return-await": "off",
"typescript/use-unknown-in-catch-callback-variable": "off",
"unicorn/prefer-add-event-listener": "off"
"tailwindcss/no-unknown-classes": "off"
}
}
]
Expand Down
10 changes: 6 additions & 4 deletions agent/channels/eve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,19 @@ export default eveChannel({

function sessionIdFromPath(pathname: string) {
const match = /^\/eve\/v1\/session\/([^/]+)/.exec(pathname);
if (!match?.[1]) return;
if (!match?.[1]) return undefined;
try {
return decodeURIComponent(match[1]);
} catch {
return;
return undefined;
}
}

async function requestIdentityFromRequest(request: Request) {
const session = await getAuthSession(request.headers);
if (!session) return;
if (!session) return undefined;
const phoneNumber = z.string().safeParse(session.user.phoneNumber);
if (!phoneNumber.success) return;
if (!phoneNumber.success) return undefined;

return {
phoneNumber: phoneNumber.data,
Expand All @@ -55,9 +55,11 @@ async function requestIdentityFromRequest(request: Request) {
}

async function waitForSessionOwnership(scope: AccessScope, sessionId: string) {
/* oxlint-disable eslint/no-await-in-loop -- Ownership visibility is checked by a bounded sequential retry loop. */
for (let attempt = 0; attempt < 5; attempt += 1) {
if (await isSessionOwned(scope, sessionId)) return true;
await new Promise((resolve) => setTimeout(resolve, 50));
}
/* oxlint-enable eslint/no-await-in-loop */
return false;
}
2 changes: 2 additions & 0 deletions agent/channels/linq.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,15 @@ async function postLinqReply(
if (files.length > 0) await thread.post({ files, markdown: "" });
return;
}
/* oxlint-disable eslint/no-await-in-loop -- Reply bubbles must be posted in conversational order. */
for (const [index, bubble] of bubbles.entries()) {
if (index === bubbles.length - 1 && files.length > 0) {
await thread.post({ files, markdown: bubble });
} else {
await thread.post({ markdown: bubble });
}
}
/* oxlint-enable eslint/no-await-in-loop */
}

const credentials: LinqChannelCredentials = env.LINQ_CONNECTOR
Expand Down
4 changes: 2 additions & 2 deletions agent/lib/google-workspace/calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ export function parseCalendarAvailability(
value: calendar_v3.Schema$FreeBusyResponse
) {
const failures = Object.entries(value.calendars ?? {}).flatMap(
([calendarId, calendar]) =>
(calendar.errors ?? []).map(
([calendarId, calendarResult]) =>
(calendarResult.errors ?? []).map(
(error) => `${calendarId}: ${error.reason ?? error.domain ?? "unknown"}`
)
);
Expand Down
12 changes: 7 additions & 5 deletions agent/lib/google-workspace/gmail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,12 @@ export async function readGmailThread(ctx: ToolContext, threadId: string) {
);
return {
id: thread.id ?? threadId,
messages: (thread.messages ?? []).slice(-20).map((message) => ({
...minimizeMessage(message),
attachments: collectAttachments(message.payload),
body: redactGoogleText(plainText(message.payload)),
})),
messages: (thread.messages ?? []).slice(-20).map((message) =>
Object.assign({}, minimizeMessage(message), {
attachments: collectAttachments(message.payload),
body: redactGoogleText(plainText(message.payload)),
})
),
};
});
}
Expand Down Expand Up @@ -162,6 +163,7 @@ export function gmailUpdateLabels(action: GmailUpdateAction) {
case "unstar":
return { addLabelIds: [], removeLabelIds: ["STARRED"] };
}
throw new Error("Unsupported Gmail update action.");
}

function header(part: GmailPart | undefined, name: string) {
Expand Down
14 changes: 8 additions & 6 deletions agent/lib/linq-browser-image-delivery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,29 +80,31 @@ async function readLinqBrowserImage(
!artifact.filename ||
!artifact.mediaType
)
return;
if (!env.BLOB_STORE_ID && !env.BLOB_READ_WRITE_TOKEN) return;
return undefined;
if (!env.BLOB_STORE_ID && !env.BLOB_READ_WRITE_TOKEN) return undefined;
const result = await get(artifact.storagePathname, {
access: "private",
abortSignal: options.signal,
});
if (result?.statusCode !== 200) return;
if (result?.statusCode !== 200) return undefined;
if (
result.blob.size !== artifact.byteSize ||
result.blob.contentType !== artifact.mediaType
)
return;
return undefined;
const reader = result.stream.getReader();
const chunks: Uint8Array[] = [];
let total = 0;
try {
/* oxlint-disable eslint/no-await-in-loop -- Blob response chunks form an ordered stream. */
for (;;) {
const { done, value } = await reader.read();
if (done) break;
total += value.byteLength;
if (total > maximumBrowserImageBytes) return;
if (total > maximumBrowserImageBytes) return undefined;
chunks.push(value);
}
/* oxlint-enable eslint/no-await-in-loop */
} finally {
reader.releaseLock();
}
Expand All @@ -113,7 +115,7 @@ async function readLinqBrowserImage(
offset += chunk.byteLength;
}
if (createHash("sha256").update(bytes).digest("hex") !== artifact.contentHash)
return;
return undefined;
return {
bytes,
filename: artifact.filename,
Expand Down
6 changes: 6 additions & 0 deletions agent/subagents/worker/lib/autofill/native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export async function fillWithKernelNativeAutofill({
}

let lastError: unknown;
/* oxlint-disable eslint/no-await-in-loop -- Autofill tries controls in priority order and stops after the first accepted target. */
for (const control of controls) {
try {
await markNativeAutofilledControls(connection, control);
Expand All @@ -175,6 +176,7 @@ export async function fillWithKernelNativeAutofill({
}
return { filledClaims: claims.length, origin };
}
/* oxlint-enable eslint/no-await-in-loop */

throw new Error(
"Chromium could not autofill any visible control. Focus a field in the intended card or address form and retry.",
Expand Down Expand Up @@ -208,12 +210,14 @@ async function fillNativeLoginControls(
);
}

/* oxlint-disable eslint/no-await-in-loop -- Login fields must be filled in DOM order so page validation sees coherent intermediate state. */
for (const { control, value } of fills) {
const accepted = await fillNativeLoginControl(connection, control, value);
if (!accepted) {
throw new Error("The login form rejected secure credential autofill.");
}
}
/* oxlint-enable eslint/no-await-in-loop */
return fills.length;
}

Expand Down Expand Up @@ -552,6 +556,7 @@ async function withKernelPage<T>(
const iframeTargets = targetInfos.filter(
({ targetId, type }) => type === "iframe" && frameIds.has(targetId)
);
/* oxlint-disable eslint/no-await-in-loop -- CDP target attachment mutates one connection and session IDs are collected in target order. */
for (const iframeTarget of iframeTargets) {
const attached = attachedTargetSchema.safeParse(
await connection
Expand All @@ -563,6 +568,7 @@ async function withKernelPage<T>(
);
if (attached.success) sessionIds.push(attached.data.sessionId);
}
/* oxlint-enable eslint/no-await-in-loop */

return await operation({
connection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,9 +309,9 @@ describe("vault browser autofill", () => {

it("lets a vault-owned adapter supply masked suggestions and claims", async () => {
const adapter: AutofillVaultAdapter = {
async listSuggestions(_scope, origin, surface) {
async listSuggestions(_scope, origin, targetSurface) {
expect(origin).toBe("https://merchant.example");
expect(surface.kind).toBe("payment-card");
expect(targetSurface.kind).toBe("payment-card");
return [
{
candidateId: "opaque-card",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* oxlint-disable vitest/require-mock-type-parameters -- The hoisted Kernel fake records cleanup request options. */
import { describe, expect, it, vi } from "vitest";
import { withVaultScreenshotMask } from "../vault-screenshot-mask";

Expand Down
Loading