Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a61cd90
chore: housekeeping
SukkaW Aug 25, 2026
e6071de
chore(lint): drop eslint bulk suppressions
SukkaW Aug 28, 2026
8ac17fe
chore(lint): migrate disable directives to vibe-proof ids, drop unuse…
SukkaW Aug 28, 2026
102272b
chore(lint): auto-fix vibe-proof and sukka error rules
SukkaW Aug 28, 2026
d25939a
chore(lint): auto-fix warning-level rules
SukkaW Aug 28, 2026
9c4740c
chore(lint): indexed array loops in presentation
SukkaW Aug 28, 2026
dee3e3a
chore(lint): indexed array loops in agent-adapter and assets
SukkaW Aug 28, 2026
4082b91
fix(desktop): move panel host attach into callback refs; document del…
SukkaW Aug 28, 2026
f17cb12
chore(lint): manual lint fixes in presentation
SukkaW Aug 28, 2026
0972a2f
chore(lint): indexed array loops in engine and sim
SukkaW Aug 28, 2026
aa3deaa
chore(lint): manual lint fixes in engine
SukkaW Aug 28, 2026
2009420
chore(lint): manual lint fixes across apps, foundation, and client
SukkaW Aug 28, 2026
5f20725
chore(lint): indexed array loops in client core and workbench
SukkaW Aug 28, 2026
72a5967
chore(lint): indexed array loops in foundation packages
SukkaW Aug 28, 2026
067ee71
chore(lint): indexed array loops in apps, integrations, and ipc
SukkaW Aug 28, 2026
83d2634
chore(lint): manual lint fixes in agent-adapter and assets
SukkaW Aug 28, 2026
3dc5c33
chore(lint): fix straggler curly and component-const errors
SukkaW Aug 28, 2026
fd13252
chore(lint): key variants reduce accumulator in model-probe test
SukkaW Aug 28, 2026
ebd09b9
fix(desktop): use useCallback for panel host attach refs
SukkaW Aug 28, 2026
52cf264
chore(lint): fix prefer-single-call, iteration-fallback-style, and pr…
SukkaW Aug 28, 2026
96df5ef
chore(lint): fix fromEntries+filter cases caught by updated prefer-ar…
SukkaW Aug 28, 2026
b96a69d
chore(assets): regenerate pi closure manifest from lockfile
SukkaW Aug 28, 2026
0290143
fix(common): resolve artifact paths against the canonicalized root
SukkaW Aug 28, 2026
e52164a
chore(lint): address review: guard model_usage keys, drop dead split0…
SukkaW Aug 28, 2026
493cc82
Update packages/host/agent-adapter/src/native/claude-code.ts
SukkaW Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/scripts/brand-matrix.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const RE_MISMATCHED_PUBLICATION_SOURCE = /must equal sourceGitSha/;
const RE_LEGACY_DESKTOP_UPLOAD = /"s3:\/\/\$\{R2_BUCKET\}\/\$\{R2_PREFIX\}\/"/;
const RE_LEGACY_RELEASE_TAG = /must equal refs\/tags\/v1\.2\.3/;
const RE_SECRETS_EXPRESSION = /secrets(?:\.|\[)/;
const ACTIONS_EXPRESSION = String.fromCodePoint(36);
const ACTIONS_EXPRESSION = String.fromCharCode(36);

function sha(character) {
return character.repeat(64);
Expand Down
39 changes: 20 additions & 19 deletions .github/scripts/release-inputs.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -110,25 +110,26 @@ describe('validateReleaseInputs', () => {
});

it('rejects malformed desktop notarization key material', () => {
const env = Object.fromEntries(
[
'APPLE_API_KEY_BASE64',
'APPLE_API_KEY_ID',
'APPLE_API_ISSUER',
'APPLE_TEAM_ID',
'AZURE_CERTIFICATE_PROFILE',
'AZURE_CLIENT_ID',
'AZURE_CODE_SIGNING_ACCOUNT',
'AZURE_PUBLISHER_NAME',
'AZURE_SIGN_ENDPOINT',
'AZURE_TENANT_ID',
'MACOS_CSC_KEY_PASSWORD',
'MACOS_CSC_LINK',
'POSTHOG_HOST',
'POSTHOG_PROJECT_TOKEN',
'SENTRY_DSN_DESKTOP',
].map((name) => [name, 'set']),
);
const env = [
'APPLE_API_KEY_BASE64',
'APPLE_API_KEY_ID',
'APPLE_API_ISSUER',
'APPLE_TEAM_ID',
'AZURE_CERTIFICATE_PROFILE',
'AZURE_CLIENT_ID',
'AZURE_CODE_SIGNING_ACCOUNT',
'AZURE_PUBLISHER_NAME',
'AZURE_SIGN_ENDPOINT',
'AZURE_TENANT_ID',
'MACOS_CSC_KEY_PASSWORD',
'MACOS_CSC_LINK',
'POSTHOG_HOST',
'POSTHOG_PROJECT_TOKEN',
'SENTRY_DSN_DESKTOP',
].reduce((acc, name) => {
acc[name] = 'set';
return acc;
}, {});
expect(() => validateReleaseInputs({ env, phase: 'sign', platform: 'desktop' })).toThrow(
RE_INVALID_KEY,
);
Expand Down
4 changes: 3 additions & 1 deletion apps/daemon/e2e/startup.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ async function main(): Promise<void> {
.map((row) => (row as { name: string }).name),
);
sqlite.close();
for (const table of ['__drizzle_migrations', 'sessions', 'workspaces', 'schedules', 'loops']) {
const expectedTables = ['__drizzle_migrations', 'sessions', 'workspaces', 'schedules', 'loops'];
for (let i = 0, len = expectedTables.length; i < len; i++) {
const table = expectedTables[i];
assert(tables.has(table), `missing migrated table ${table}`);
}

Expand Down
2 changes: 1 addition & 1 deletion apps/daemon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@sentry/node": "10.62.0",
"@sentry/profiling-node": "10.62.0",
"better-sqlite3": "^13.0.2",
"foxts": "^5.8.0",
"foxts": "^5.9.1",
"pino": "^10.3.1",
"zod": "catalog:"
},
Expand Down
6 changes: 5 additions & 1 deletion apps/daemon/scripts/dev-clean.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ import { databasePath, runtimeFilePath } from '../src/config';

// Resolves through config.ts so a fork's renamed state dir, the resolved channel, or an active
// LINKCODE_PROFILE cleans the same universe the dev daemon will actually use.
for (const path of [databasePath(), runtimeFilePath()]) rmSync(path, { force: true });
const devStatePaths = [databasePath(), runtimeFilePath()];
for (let i = 0, len = devStatePaths.length; i < len; i++) {
const path = devStatePaths[i];
rmSync(path, { force: true });
}
11 changes: 8 additions & 3 deletions apps/daemon/scripts/package-daemon.mts
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,13 @@ run('pnpm', ['--filter', '@linkcode/daemon', '--prod', 'deploy', '--legacy', out
rmSync(join(outDir, 'dist'), { recursive: true, force: true });
cpSync(join(daemonDir, 'dist'), join(outDir, 'dist'), { recursive: true });

for (const [scope, prefix] of PRUNE) {
for (let i = 0, len = PRUNE.length; i < len; i++) {
const [scope, prefix] = PRUNE[i];
const scopeDir = join(outDir, 'node_modules', scope);
if (!existsSync(scopeDir)) continue;
for (const entry of readdirSync(scopeDir)) {
const scopeEntries = readdirSync(scopeDir);
for (let j = 0, entryCount = scopeEntries.length; j < entryCount; j++) {
const entry = scopeEntries[j];
if (entry.startsWith(prefix)) rmSync(join(scopeDir, entry), { recursive: true, force: true });
}
}
Expand All @@ -74,7 +77,9 @@ console.log(`daemon packaged at ${outDir} (${Math.round(bytes / 1e6)} MB)`);

function dirSize(dir: string): number {
let total = 0;
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const entries = readdirSync(dir, { withFileTypes: true });
for (let i = 0, len = entries.length; i < len; i++) {
const entry = entries[i];
const full = join(dir, entry.name);
if (entry.isDirectory()) total += dirSize(full);
else if (entry.isFile()) total += statSync(full).size;
Expand Down
15 changes: 12 additions & 3 deletions apps/daemon/src/__tests__/ai-gateway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,22 @@ class FakeChild implements SidecarChildProcess {
this.signal = signal;
}
emitStdout(text: string): void {
for (const listener of this.dataListeners) listener(text);
for (let i = 0, len = this.dataListeners.length; i < len; i++) {
const listener = this.dataListeners[i];
listener(text);
}
}
emitError(error: Error): void {
for (const listener of this.errorListeners) listener(error);
for (let i = 0, len = this.errorListeners.length; i < len; i++) {
const listener = this.errorListeners[i];
listener(error);
}
}
emitExit(code: number | null): void {
for (const listener of this.exitListeners) listener(code);
for (let i = 0, len = this.exitListeners.length; i < len; i++) {
const listener = this.exitListeners[i];
listener(code);
}
}
}

Expand Down
5 changes: 3 additions & 2 deletions apps/daemon/src/__tests__/secrets-vault.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ const noKeyring = (): MasterKey | null => null;
* Secrets are only reachable through a namespace, so every case goes through one. On disk the same
* entry is the full `cloud:session` ref — asserting against that is what proves the prefixing.
*/
const open = (file: string, loadKey: () => MasterKey | null): SecretStore =>
createSecretVault(file, loadKey).namespace('cloud');
function open(file: string, loadKey: () => MasterKey | null): SecretStore {
return createSecretVault(file, loadKey).namespace('cloud');
}

let file: string;
const realPlatform = process.platform;
Expand Down
5 changes: 4 additions & 1 deletion apps/daemon/src/ai-gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,10 @@ export function createAiGatewaySidecar(options: AiGatewaySidecarOptions = {}): T
},
async closeAll() {
const entries = [...running.values()];
for (const entry of entries) entry.close();
for (let i = 0, len = entries.length; i < len; i++) {
const entry = entries[i];
entry.close();
}
await Promise.allSettled(entries.map((entry) => entry.ready));
},
};
Expand Down
10 changes: 7 additions & 3 deletions apps/daemon/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ function parseAccounts(store: SecretStore, raw: unknown): Parsed<Accounts> {
}
const accounts: Accounts = [];
let migrated = false;
for (const value of raw) {
for (let i = 0, len = raw.length; i < len; i++) {
const value = raw[i];
// The credential secret lives in the vault (CODE-371); merge it back before validating, so a
// secret that is gone fails the schema and lands in the same drop-and-log path as a malformed one.
const attached = withAccountSecret(store, value);
Expand Down Expand Up @@ -267,7 +268,8 @@ function parseCustomMcpServers(store: SecretStore, raw: unknown): Parsed<CustomM
}
const servers: CustomMcpServer[] = [];
let migrated = false;
for (const value of snapshot.servers) {
for (let i = 0, len = snapshot.servers.length; i < len; i++) {
const value = snapshot.servers[i];
const attached = withCustomMcpSecrets(store, value, snapshot.generation);
migrated ||= attached.migrated;
const server = CustomMcpServerSchema.safeParse(attached.value);
Expand All @@ -292,7 +294,9 @@ function parseProviders(store: SecretStore, raw: unknown): Parsed<ProvidersConfi
}
const providers: ProvidersConfig = {};
let migrated = false;
for (const [key, value] of Object.entries(raw)) {
const entries = Object.entries(raw);
for (let i = 0, len = entries.length; i < len; i++) {
const [key, value] = entries[i];
const kind = AgentKindSchema.safeParse(key);
if (!kind.success) {
logger.warn(
Expand Down
13 changes: 10 additions & 3 deletions apps/daemon/src/diagnostic-sanitizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ function sanitizeValue(value: unknown, seen: WeakMap<object, unknown>): unknown
seen.set(value, sanitized);
const cause = Reflect.get(value, 'cause');
if (cause !== undefined) sanitized.cause = sanitizeValue(cause, seen);
for (const [key, entry] of Object.entries(value)) {
const errorEntries = Object.entries(value);
for (let i = 0, len = errorEntries.length; i < len; i++) {
const [key, entry] = errorEntries[i];
sanitized[key] = SENSITIVE_KEY.test(key) ? REDACTED : sanitizeValue(entry, seen);
}
return sanitized;
Expand All @@ -49,7 +51,10 @@ function sanitizeValue(value: unknown, seen: WeakMap<object, unknown>): unknown
if (Array.isArray(value)) {
const sanitized: unknown[] = [];
seen.set(value, sanitized);
for (const entry of value) sanitized.push(sanitizeValue(entry, seen));
for (let i = 0, len = value.length; i < len; i++) {
const entry = value[i];
sanitized.push(sanitizeValue(entry, seen));
}
return sanitized;
}

Expand All @@ -59,7 +64,9 @@ function sanitizeValue(value: unknown, seen: WeakMap<object, unknown>): unknown

const sanitized: Record<string, unknown> = {};
seen.set(value, sanitized);
for (const [key, entry] of Object.entries(value)) {
const entries = Object.entries(value);
for (let i = 0, len = entries.length; i < len; i++) {
const [key, entry] = entries[i];
sanitized[key] = SENSITIVE_KEY.test(key) ? REDACTED : sanitizeValue(entry, seen);
}
return sanitized;
Expand Down
38 changes: 21 additions & 17 deletions apps/daemon/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
} from './config';
import { DaemonLoggerLive, logger } from './logger';
import { createLoopStore } from './loop-store';
import type { ManagedAgentKind } from './managed-agent-refresh';
import { agentsToRefresh, consentedManagedAgents } from './managed-agent-refresh';
import { daemonStateDir } from './paths';
import { createProviderConfigStore } from './provider-store';
Expand Down Expand Up @@ -187,6 +188,23 @@ async function main(): Promise<void> {
);
const assets = new AssetManager();
const consentedAgents = consentedManagedAgents(assets);
const refreshAgentRuntime = (kind: ManagedAgentKind): Promise<void> =>
assets
.ensure(managedAgentAssetId(kind))
.catch((err) => {
logger.warn(
{ err, agentKind: kind, operation: 'asset.ensure' },
'Managed agent install failed',
);
})
.then((installed) => {
if (installed) {
logger.info(
{ agentKind: kind, operation: 'asset.ensure' },
'Managed agent runtime ready',
);
}
});
const gc = assets.gcAtBoot();
if (gc.removed.length > 0) {
yield* Effect.logInfo('Removed superseded managed assets', {
Expand Down Expand Up @@ -285,23 +303,9 @@ async function main(): Promise<void> {
const engine = yield* EngineService;
void agentRuntimesReady
.then((agentRuntimes) => {
for (const kind of agentsToRefresh(consentedAgents, agentRuntimes, assets)) {
void assets
.ensure(managedAgentAssetId(kind))
.catch((err) => {
logger.warn(
{ err, agentKind: kind, operation: 'asset.ensure' },
'Managed agent install failed',
);
})
.then((installed) => {
if (installed) {
logger.info(
{ agentKind: kind, operation: 'asset.ensure' },
'Managed agent runtime ready',
);
}
});
const refreshable = agentsToRefresh(consentedAgents, agentRuntimes, assets);
for (let i = 0, len = refreshable.length; i < len; i++) {
void refreshAgentRuntime(refreshable[i]);
}
})
.catch((err) => {
Expand Down
8 changes: 5 additions & 3 deletions apps/daemon/src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function createDaemonLogger(
name: 'linkcode-daemon',
hooks: {
logMethod(args, method) {
for (let index = 0; index < args.length; index += 1) {
for (let index = 0, len = args.length; index < len; index += 1) {
args[index] = sanitizeDiagnostic(args[index]);
}
method.apply(this, args);
Expand All @@ -78,9 +78,11 @@ export const logger = createDaemonLogger();

function effectBindings(messages: readonly unknown[]): Record<string, string | number> {
const bindings: Record<string, string | number> = { source: 'effect' };
for (const message of messages) {
for (let i = 0, len = messages.length; i < len; i++) {
const message = messages[i];
if (typeof message !== 'object' || message === null) continue;
for (const key of EFFECT_BINDING_KEYS) {
for (let j = 0, keyCount = EFFECT_BINDING_KEYS.length; j < keyCount; j++) {
const key = EFFECT_BINDING_KEYS[j];
const value = Reflect.get(message, key);
if (typeof value === 'string' || typeof value === 'number') bindings[key] = value;
}
Expand Down
3 changes: 2 additions & 1 deletion apps/daemon/src/loop-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ export function createLoopStore(dbPath: string): LoopStore {
/** Parse each row through its schema; drop and log a row that fails rather than failing the load. */
function parseAll<Row, T>(rows: Row[], parse: (row: Row) => T): T[] {
const parsed: T[] = [];
for (const row of rows) {
for (let i = 0, len = rows.length; i < len; i++) {
const row = rows[i];
try {
parsed.push(parse(row));
} catch {
Expand Down
4 changes: 3 additions & 1 deletion apps/daemon/src/pty/bench-throughput.mts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ async function runLinkCodePty(
const pending = new Map<string, { resolve(ms: number): void; reject(error: Error): void }>();

child.stdout.on('data', (chunk: Buffer) => {
for (const frame of decoder.feed(chunk)) {
const frames = decoder.feed(chunk);
for (let i = 0, len = frames.length; i < len; i++) {
const frame = frames[i];
switch (frame.type) {
case OPENED:
case OUTPUT:
Expand Down
16 changes: 13 additions & 3 deletions apps/daemon/src/pty/sidecar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,11 @@ export class SidecarPtyBackend implements PtyBackend {
this.child = child;
child.stdout.on('data', (chunk: Buffer) => {
try {
for (const frame of this.decoder.feed(chunk)) this.handleFrame(frame);
const frames = this.decoder.feed(chunk);
for (let i = 0, len = frames.length; i < len; i++) {
const frame = frames[i];
this.handleFrame(frame);
}
} catch (err) {
logger.error(
{
Expand Down Expand Up @@ -219,7 +223,10 @@ export class SidecarPtyBackend implements PtyBackend {
const unsub = terminal.exited ? noop : terminal.data.add(cb);
if (!terminal.dataSubscribed) {
terminal.dataSubscribed = true;
for (const data of terminal.bufferedData) cb(data);
for (let i = 0, len = terminal.bufferedData.length; i < len; i++) {
const data = terminal.bufferedData[i];
cb(data);
}
terminal.bufferedData.length = 0;
}
return unsub;
Expand Down Expand Up @@ -271,7 +278,10 @@ export class SidecarPtyBackend implements PtyBackend {
}
this.pending.clear();
const terminalIds = Array.from(this.terminals.keys());
for (const terminalId of terminalIds) this.finish(terminalId, null, false);
for (let i = 0, len = terminalIds.length; i < len; i++) {
const terminalId = terminalIds[i];
this.finish(terminalId, null, false);
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion apps/daemon/src/schedule-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ export function createScheduleStore(dbPath: string): ScheduleStore {
/** Parse each row through its schema; drop and log a row that fails rather than failing the load. */
function parseAll<Row, T>(rows: Row[], parse: (row: Row) => T): T[] {
const parsed: T[] = [];
for (const row of rows) {
for (let i = 0, len = rows.length; i < len; i++) {
const row = rows[i];
try {
parsed.push(parse(row));
} catch {
Expand Down
8 changes: 6 additions & 2 deletions apps/daemon/src/secrets/custom-mcp-credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ export function withCustomMcpSecrets(
}
const values = { ...(server[field] as Record<string, unknown>) };
let migrated = false;
for (const [key, value] of Object.entries(values)) {
const valueEntries = Object.entries(values);
for (let i = 0, len = valueEntries.length; i < len; i++) {
const [key, value] = valueEntries[i];
if (typeof value === 'string') {
store.set(secretKey(generation, entry.id, field, key), value);
migrated = true;
Expand All @@ -52,7 +54,9 @@ export function detachCustomMcpSecrets(
const values = entry.server.type === 'stdio' ? entry.server.env : entry.server.headers;
if (values === undefined) return entry;
const placeholders: Record<string, null> = {};
for (const [key, value] of Object.entries(values)) {
const valueEntries = Object.entries(values);
for (let i = 0, len = valueEntries.length; i < len; i++) {
const [key, value] = valueEntries[i];
secrets.set(secretKey(generation, entry.id, field, key), value);
placeholders[key] = null;
}
Expand Down
Loading
Loading