Skip to content
Open
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
2 changes: 1 addition & 1 deletion packages/compactor/src/dictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function generateCodes(n: number): string[] {
// 2-letter codes: $AA .. $ZZ (676)
for (let i = 0; i < 26 && codes.length < n; i++) {
for (let j = 0; j < 26 && codes.length < n; j++) {
codes.push('$' + String.fromCharCode(A + i) + String.fromCharCode(A + j));
codes.push(`$${String.fromCharCode(A + i)}${String.fromCharCode(A + j)}`);
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/compactor/src/optimizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const ITALIC_RE = /(?<!\*)\*([^*]+?)\*(?!\*)/g;
const TRIVIAL_CODE_RE = /`([a-zA-Z0-9_.-]+)`/g;
const TABLE_SEP_RE = /^[\s|:-]+$/;
const MULTI_SPACE_RE = / {2,}/g;
const LEADING_SPACES_RE = /^( {4,})/gm;
const _LEADING_SPACES_RE = /^( {4,})/gm;
const BULLET_RE = /^(\s*[-*+])\s+(.*)/;

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -106,7 +106,7 @@ export function compactBullets(text: string): string {
const fullPrefix = m[1];
const indentMatch = fullPrefix.match(/^(\s*)(.+)/);
const indent = indentMatch ? indentMatch[1] : '';
const prefix = indentMatch ? indentMatch[2] + ' ' : '- ';
const prefix = indentMatch ? `${indentMatch[2]} ` : '- ';
bulletRun.push({ text: m[2], prefix, indent });
} else {
flush();
Expand Down
5 changes: 3 additions & 2 deletions packages/compactor/src/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

// Matches most emoji: emoticons, dingbats, symbols, skin tones, ZWJ sequences
const EMOJI_REGEX =
// biome-ignore lint/suspicious/noMisleadingCharacterClass: intentional emoji range including combining chars
/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{FE00}-\u{FE0F}\u{1F900}-\u{1F9FF}\u{1FA00}-\u{1FA6F}\u{1FA70}-\u{1FAFF}\u{200D}\u{20E3}\u{E0020}-\u{E007F}]/gu;

// Markdown header
Expand Down Expand Up @@ -177,7 +178,7 @@ export function removeEmptySections(text: string): string {
const body = sec.bodyLines.join('\n').trim();
if (!sec.header && !body) continue;
if (sec.header && !body && !hasChild[i]) continue; // empty section, no children
if (sec.header) result.push('#'.repeat(sec.level) + ' ' + sec.header);
if (sec.header) result.push(`${'#'.repeat(sec.level)} ${sec.header}`);
if (body) result.push(body);
result.push(''); // blank line between sections
}
Expand Down Expand Up @@ -221,7 +222,7 @@ export function compressMarkdownTable(text: string): string {
if (headers.length >= 5) {
// Wide tables: preserve rows without header/separator
for (const row of rows) {
result.push('| ' + row.join(' | ') + ' |');
result.push(`| ${row.join(' | ')} |`);
}
} else if (headers.length === 2) {
// 2-column: key: value format
Expand Down
20 changes: 10 additions & 10 deletions packages/compactor/tests/compactor.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
import { existsSync, unlinkSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { createDatabase } from '@tinyclaw/core';
import type { Database } from '@tinyclaw/types';
import { existsSync, unlinkSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { createCompactor } from '../src/compactor.js';

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -98,9 +98,9 @@ describe('createCompactor', () => {

const result = await compactor.compactIfNeeded('user1', provider);
expect(result).not.toBeNull();
expect(result!.summary.l2).toContain('TypeScript');
expect(result!.metrics.messagesBefore).toBe(10);
expect(result!.metrics.messagesKept).toBe(3);
expect(result?.summary.l2).toContain('TypeScript');
expect(result?.metrics.messagesBefore).toBe(10);
expect(result?.metrics.messagesKept).toBe(3);
});

it('saves compaction and allows retrieval', async () => {
Expand Down Expand Up @@ -141,7 +141,7 @@ describe('createCompactor', () => {

const result = await compactor.compactIfNeeded('user1', provider);
expect(result).not.toBeNull();
expect(result!.metrics.messagesKept).toBe(1);
expect(result?.metrics.messagesKept).toBe(1);
});

it('returns metrics with compression ratio', async () => {
Expand All @@ -154,9 +154,9 @@ describe('createCompactor', () => {

const result = await compactor.compactIfNeeded('user1', provider);
expect(result).not.toBeNull();
expect(result!.metrics.compressionRatio).toBeGreaterThan(0);
expect(result!.metrics.compressionRatio).toBeLessThanOrEqual(1);
expect(result!.metrics.durationMs).toBeGreaterThanOrEqual(0);
expect(result?.metrics.compressionRatio).toBeGreaterThan(0);
expect(result?.metrics.compressionRatio).toBeLessThanOrEqual(1);
expect(result?.metrics.durationMs).toBeGreaterThanOrEqual(0);
});

it('handles provider failure gracefully', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/config/src/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function createConfigTools(manager: ConfigManager): Tool[] {
const trimmed = raw.trim();
if (trimmed === 'true') value = true;
else if (trimmed === 'false') value = false;
else if (trimmed !== '' && !isNaN(Number(trimmed))) value = Number(trimmed);
else if (trimmed !== '' && !Number.isNaN(Number(trimmed))) value = Number(trimmed);
else {
try {
value = JSON.parse(trimmed);
Expand Down
12 changes: 6 additions & 6 deletions packages/core/src/database.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Database as BunDatabase, type SQLQueryBindings } from 'bun:sqlite';
import { mkdirSync } from 'node:fs';
import { dirname } from 'node:path';
import type {
BackgroundTask,
BlackboardEntry,
Expand All @@ -11,14 +13,12 @@ import type {
SubAgentRecord,
TaskMetricRecord,
} from '@tinyclaw/types';
import { mkdirSync } from 'fs';
import { dirname } from 'path';

export function createDatabase(path: string): Database {
// Ensure directory exists
try {
mkdirSync(dirname(path), { recursive: true });
} catch (err) {
} catch (_err) {
// Directory might already exist
}

Expand Down Expand Up @@ -336,12 +336,12 @@ export function createDatabase(path: string): Database {
LIMIT ?
`);

const updateEpisodicAccessStmt = db.prepare(`
const _updateEpisodicAccessStmt = db.prepare(`
UPDATE episodic_memory SET access_count = access_count + 1, last_accessed_at = ?
WHERE id = ?
`);

const pruneEpisodicEventsStmt = db.prepare(`
const _pruneEpisodicEventsStmt = db.prepare(`
DELETE FROM episodic_memory
WHERE user_id = ? AND importance < ? AND access_count <= ? AND created_at < ?
`);
Comment on lines +339 to 347
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These prepared statements are created (executing db.prepare) but never used. Besides being dead code, this adds unnecessary work during DB initialization. Either remove them or integrate them into the database API methods that need access-count updates / pruning.

Copilot uses AI. Check for mistakes.
Expand Down Expand Up @@ -793,7 +793,7 @@ export function createDatabase(path: string): Database {
const tags = `${record.eventType} ${record.userId}`;
insertFTSStmt.run(
record.id,
record.content + (record.outcome ? ' ' + record.outcome : ''),
record.content + (record.outcome ? ` ${record.outcome}` : ''),
tags,
);
},
Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/loop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type {
AgentContext,
Message,
PendingApproval,
ShieldDecision,
ShieldEvent,
ToolCall,
} from '@tinyclaw/types';
Expand Down Expand Up @@ -306,7 +305,7 @@ function emitDelegationComplete(
// delegate_to_existing: "... (<uuid>) [task: <uuid>] ..."
// delegate_tasks: multi-line output with multiple agent:/task: pairs
const UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;
const allUUIDs = result.match(UUID_RE) || [];
const _allUUIDs = result.match(UUID_RE) || [];

// For delegate_task: "agent: <uuid>, task: <uuid>" → agentId is matched first, taskId second
// For delegate_background: "[id: <taskId>]\nSub-agent: Role (<agentId>)" → taskId first, agentId second
Expand Down Expand Up @@ -1001,7 +1000,7 @@ export async function agentLoop(
// can craft a natural, conversational response instead of the
// generic "Done!" that was causing a feedback loop in the history.
const writeResult = toolResults[0]?.result || 'completed';
const writeSummary = summarizeToolResults([toolCall], toolResults);
const _writeSummary = summarizeToolResults([toolCall], toolResults);
messages.push({
role: 'assistant',
content: `I used ${toolCall.name} and the result was: ${writeResult}`,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/update-checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export function isNewerVersion(current: string, latest: string): boolean {
.split('.')
.map((s) => {
const n = Number(s);
return isNaN(n) ? 0 : n;
return Number.isNaN(n) ? 0 : n;
})
.slice(0, 3);
const [cMaj = 0, cMin = 0, cPat = 0] = parse(current);
Expand Down
14 changes: 7 additions & 7 deletions packages/core/tests/update-checker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
*/

import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import {
buildUpdateContext,
checkForUpdate,
Expand Down Expand Up @@ -197,8 +197,8 @@ describe('checkForUpdate', () => {

const result = await checkForUpdate('1.0.0', tempDir);
expect(result).not.toBeNull();
expect(result!.latest).toBe('1.1.0');
expect(result!.updateAvailable).toBe(true);
expect(result?.latest).toBe('1.1.0');
expect(result?.updateAvailable).toBe(true);
});

test('re-evaluates updateAvailable against current version', async () => {
Expand All @@ -208,8 +208,8 @@ describe('checkForUpdate', () => {

const result = await checkForUpdate('1.1.0', tempDir);
expect(result).not.toBeNull();
expect(result!.updateAvailable).toBe(false);
expect(result!.current).toBe('1.1.0');
expect(result?.updateAvailable).toBe(false);
expect(result?.current).toBe('1.1.0');
});

test('returns null on network failure with no cache', async () => {
Expand Down
9 changes: 1 addition & 8 deletions packages/delegation/src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,10 @@
*/

import { logger } from '@tinyclaw/logger';
import type { Message, Provider, Tool } from '@tinyclaw/types';
import { runSubAgentV2 } from './runner.js';
import type { DelegationIntercom, DelegationQueue, DelegationStore } from './store.js';
import type { TimeoutEstimator } from './timeout-estimator.js';
import type {
BackgroundRunner,
BackgroundTaskRecord,
LifecycleManager,
OrientationContext,
TemplateManager,
} from './types.js';
import type { BackgroundRunner, LifecycleManager, TemplateManager } from './types.js';

// ---------------------------------------------------------------------------
// Constants
Expand Down
6 changes: 3 additions & 3 deletions packages/delegation/src/compat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
*/

import { logger } from '@tinyclaw/logger';
import type { ProviderOrchestrator, QueryTier } from '@tinyclaw/router';
import type { Tool } from '@tinyclaw/types';
import type { QueryTier } from '@tinyclaw/router';
import type { Provider, Tool } from '@tinyclaw/types';
import { runSubAgent } from './runner.js';
import type { DelegationToolConfig } from './types.js';

Expand Down Expand Up @@ -84,7 +84,7 @@ export function createDelegationTool(config: DelegationToolConfig): Tool {
}

// 1. Resolve provider
let provider;
let provider: Provider;
try {
if (tierOverride) {
provider = orchestrator.getRegistry().getForTier(tierOverride as QueryTier);
Expand Down
5 changes: 2 additions & 3 deletions packages/delegation/src/lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
* and message persistence.
*/

import type { QueryTier } from '@tinyclaw/router';
import type { Message, SubAgentRecord } from '@tinyclaw/types';
import type { SubAgentRecord } from '@tinyclaw/types';
import { formatOrientation } from './orientation.js';
import type { DelegationStore } from './store.js';
import type { LifecycleManager, OrientationContext } from './types.js';
import type { LifecycleManager } from './types.js';

// ---------------------------------------------------------------------------
// Constants
Expand Down
2 changes: 1 addition & 1 deletion packages/delegation/src/orientation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,5 @@ export function formatOrientation(ctx: OrientationContext): string {

function truncate(text: string, maxChars: number): string {
if (text.length <= maxChars) return text;
return text.slice(0, maxChars) + '...';
return `${text.slice(0, maxChars)}...`;
}
16 changes: 12 additions & 4 deletions packages/delegation/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@
*/

import { logger } from '@tinyclaw/logger';
import type { Message, Provider, ShieldEngine, ShieldEvent, Tool, ToolCall } from '@tinyclaw/types';
import type {
LLMResponse,
Message,
Provider,
ShieldEngine,
ShieldEvent,
Tool,
ToolCall,
} from '@tinyclaw/types';
import { formatOrientation } from './orientation.js';
import type { TimeoutEstimator } from './timeout-estimator.js';
import type {
Expand Down Expand Up @@ -130,7 +138,7 @@ function buildSubAgentPrompt(role: string, orientation?: OrientationContext): st
let prompt = '';

if (orientation) {
prompt += formatOrientation(orientation) + '\n\n';
prompt += `${formatOrientation(orientation)}\n\n`;
}

prompt +=
Expand Down Expand Up @@ -166,7 +174,7 @@ interface AdaptiveLoopConfig {
shield?: ShieldEngine;
}

async function runAgentLoop(
async function _runAgentLoop(
provider: Provider,
tools: Tool[],
messages: Message[],
Expand Down Expand Up @@ -248,7 +256,7 @@ async function runAdaptiveAgentLoop(

iterations = i + 1;

let response;
let response: LLMResponse;
try {
response = await raceAbort(provider.chat(messages, tools));
} catch (err: any) {
Expand Down
1 change: 0 additions & 1 deletion packages/delegation/src/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
* agent learning how to hire better freelancers through experience.
*/

import type { QueryTier } from '@tinyclaw/router';
import type { RoleTemplate } from '@tinyclaw/types';
import type { DelegationStore } from './store.js';
import type { TemplateManager } from './types.js';
Expand Down
8 changes: 3 additions & 5 deletions packages/delegation/src/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ import { createBackgroundRunner } from './background.js';
import { DELEGATION_TOOL_NAMES } from './handbook.js';
import { createLifecycleManager } from './lifecycle.js';
import { buildOrientationContext } from './orientation.js';
import { runSubAgentV2 } from './runner.js';
import type { DelegationIntercom, DelegationQueue, DelegationStore } from './store.js';
import type { DelegationIntercom, DelegationQueue } from './store.js';
import { createTemplateManager } from './templates.js';
import type { TimeoutEstimator } from './timeout-estimator.js';
import type {
BackgroundRunner,
DelegationV2Config,
Expand All @@ -47,7 +45,7 @@ const DEFAULT_SAFE_TOOLS = new Set([
const MAX_BATCH_SIZE = 10;

/** Fallback background sub-agent timeout (used when no estimator). */
const BACKGROUND_TIMEOUT_MS_FALLBACK = 60_000;
const _BACKGROUND_TIMEOUT_MS_FALLBACK = 60_000;

// ---------------------------------------------------------------------------
// Helper: filter tools for sub-agents
Expand Down Expand Up @@ -787,7 +785,7 @@ export function createDelegationTools(config: DelegationToolsConfig): {
// ---------------------------------------------------------------------------

/** Extract keyword tags from role + task text. */
function extractTags(role: string, task: string): string[] {
function _extractTags(role: string, task: string): string[] {
const text = `${role} ${task}`.toLowerCase();
const words = text
.replace(/[^a-z0-9\s]/g, ' ')
Expand Down
2 changes: 1 addition & 1 deletion packages/delegation/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import type { ProviderOrchestrator, QueryTier } from '@tinyclaw/router';
import type { LearningEngine, Message, Provider, Tool } from '@tinyclaw/types';
import type { DelegationQueue, DelegationStore } from './store.js';
import type { DelegationStore } from './store.js';
import type { TimeoutEstimator } from './timeout-estimator.js';

// ---------------------------------------------------------------------------
Expand Down
Loading
Loading