Skip to content

Commit f7d933a

Browse files
ersinkocclaude
andcommitted
πŸ”– release: v0.3.0 β€” bug fixes, test repairs, formatting
- Fix 14 bugs: missing mocks, ESM require(), variable shadowing, lint warnings, vi.mock hoisting (TDZ), missing importOriginal, migration default, test coverage - Bump version 0.2.9 β†’ 0.3.0 - Fix Prettier formatting across 17 files - Add deny-escalation route tests (3 new tests) - Fix health test flaky timeout under full suite load - All 26,719 tests pass, 0 errors, 0 lint errors in production code Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a9c778d commit f7d933a

23 files changed

Lines changed: 1565 additions & 584 deletions

β€ŽREADME.mdβ€Ž

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,14 +1487,14 @@ Sliding window algorithm with configurable window (default 60s), max requests (d
14871487

14881488
### Claw Agents
14891489

1490-
| Method | Endpoint | Description |
1491-
| -------- | --------------------------------------- | ----------------------------------- |
1492-
| `GET` | `/api/v1/claws` | List all claws with session status |
1493-
| `POST` | `/api/v1/claws` | Create a new claw agent |
1494-
| `GET` | `/api/v1/claws/stats` | Aggregate claw statistics |
1495-
| `GET` | `/api/v1/claws/:id` | Get claw details + session |
1496-
| `PUT` | `/api/v1/claws/:id` | Update claw configuration |
1497-
| `DELETE` | `/api/v1/claws/:id` | Delete claw (auto-stops if running) |
1490+
| Method | Endpoint | Description |
1491+
| -------- | -------------------------------------- | ----------------------------------- |
1492+
| `GET` | `/api/v1/claws` | List all claws with session status |
1493+
| `POST` | `/api/v1/claws` | Create a new claw agent |
1494+
| `GET` | `/api/v1/claws/stats` | Aggregate claw statistics |
1495+
| `GET` | `/api/v1/claws/:id` | Get claw details + session |
1496+
| `PUT` | `/api/v1/claws/:id` | Update claw configuration |
1497+
| `DELETE` | `/api/v1/claws/:id` | Delete claw (auto-stops if running) |
14981498
| `POST` | `/api/v1/claws/:id/start` | Start claw execution |
14991499
| `POST` | `/api/v1/claws/:id/pause` | Pause running claw |
15001500
| `POST` | `/api/v1/claws/:id/resume` | Resume paused claw |
@@ -1559,19 +1559,19 @@ Sliding window algorithm with configurable window (default 60s), max requests (d
15591559

15601560
Real-time broadcasts via WebSocket at `ws://localhost:8080/ws` (attached to the HTTP server, same port):
15611561

1562-
| Event | Description |
1563-
| ------------------------- | ------------------------------------------------ |
1564-
| `data:changed` | CRUD mutation on any entity (tasks, notes, etc.) |
1565-
| `chat:stream:*` | Streaming response chunks |
1566-
| `tool:start/progress/end` | Tool execution lifecycle |
1567-
| `channel:message` | Incoming channel message (Telegram, WhatsApp) |
1568-
| `channel:status` | Channel connection/disconnection status change |
1569-
| `channel:user:*` | User events (first_seen, pending, blocked, etc.) |
1570-
| `trigger:executed` | Trigger execution result |
1571-
| `coding-agent:session:*` | Coding agent session lifecycle and output |
1572-
| `bg-agent:*` | Background agent lifecycle and cycle results |
1573-
| `subagent:*` | Subagent spawned, progress, and completion |
1574-
| `pulse:activity` | Pulse system proactive activity |
1562+
| Event | Description |
1563+
| ------------------------- | ------------------------------------------------- |
1564+
| `data:changed` | CRUD mutation on any entity (tasks, notes, etc.) |
1565+
| `chat:stream:*` | Streaming response chunks |
1566+
| `tool:start/progress/end` | Tool execution lifecycle |
1567+
| `channel:message` | Incoming channel message (Telegram, WhatsApp) |
1568+
| `channel:status` | Channel connection/disconnection status change |
1569+
| `channel:user:*` | User events (first_seen, pending, blocked, etc.) |
1570+
| `trigger:executed` | Trigger execution result |
1571+
| `coding-agent:session:*` | Coding agent session lifecycle and output |
1572+
| `bg-agent:*` | Background agent lifecycle and cycle results |
1573+
| `subagent:*` | Subagent spawned, progress, and completion |
1574+
| `pulse:activity` | Pulse system proactive activity |
15751575
| `claw:*` | Claw lifecycle, cycle results, output, escalation |
15761576

15771577
### Response Format

β€Žpackage.jsonβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ownpilot-monorepo",
3-
"version": "0.2.9",
3+
"version": "0.3.0",
44
"private": true,
55
"description": "OwnPilot - Privacy-first personal AI assistant platform",
66
"type": "module",

β€Žpackages/cli/package.jsonβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ownpilot/cli",
3-
"version": "0.2.9",
3+
"version": "0.3.0",
44
"description": "CLI for OwnPilot",
55
"type": "module",
66
"bin": {

β€Žpackages/core/package.jsonβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ownpilot/core",
3-
"version": "0.2.9",
3+
"version": "0.3.0",
44
"description": "OwnPilot Core - Secure AI agent engine and tool framework",
55
"type": "module",
66
"sideEffects": false,

β€Žpackages/gateway/package.jsonβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ownpilot/gateway",
3-
"version": "0.2.9",
3+
"version": "0.3.0",
44
"description": "HTTP API gateway for OwnPilot",
55
"type": "module",
66
"main": "./dist/index.js",

β€Žpackages/gateway/src/db/repositories/claws.test.tsβ€Ž

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,7 @@ describe('ClawsRepository', () => {
364364

365365
describe('getInterruptedSessions', () => {
366366
it('should return running/waiting sessions with configs', async () => {
367-
mockAdapter.query.mockResolvedValueOnce([
368-
{ ...makeClawRow(), session_state: 'running' },
369-
]);
367+
mockAdapter.query.mockResolvedValueOnce([{ ...makeClawRow(), session_state: 'running' }]);
370368

371369
const interrupted = await repo.getInterruptedSessions();
372370
expect(interrupted).toHaveLength(1);

β€Žpackages/gateway/src/db/repositories/claws.tsβ€Ž

Lines changed: 81 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,10 @@ export class ClawsRepository extends BaseRepository {
201201
}
202202

203203
async getById(id: string, userId: string): Promise<ClawConfig | null> {
204-
const row = await this.queryOne<ClawRow>(
205-
`SELECT * FROM claws WHERE id = $1 AND user_id = $2`,
206-
[id, userId]
207-
);
204+
const row = await this.queryOne<ClawRow>(`SELECT * FROM claws WHERE id = $1 AND user_id = $2`, [
205+
id,
206+
userId,
207+
]);
208208
return row ? rowToConfig(row) : null;
209209
}
210210

@@ -419,10 +419,9 @@ export class ClawsRepository extends BaseRepository {
419419
artifacts: string[];
420420
pendingEscalation: ClawEscalation | null;
421421
} | null> {
422-
const row = await this.queryOne<SessionRow>(
423-
`SELECT * FROM claw_sessions WHERE claw_id = $1`,
424-
[clawId]
425-
);
422+
const row = await this.queryOne<SessionRow>(`SELECT * FROM claw_sessions WHERE claw_id = $1`, [
423+
clawId,
424+
]);
426425
if (!row) return null;
427426
return {
428427
state: row.state as ClawState,
@@ -582,16 +581,18 @@ export class ClawsRepository extends BaseRepository {
582581
);
583582
}
584583

585-
async saveAuditBatch(entries: Array<{
586-
clawId: string;
587-
cycleNumber: number;
588-
toolName: string;
589-
toolArgs: Record<string, unknown>;
590-
toolResult: string;
591-
success: boolean;
592-
durationMs: number;
593-
category?: string;
594-
}>): Promise<void> {
584+
async saveAuditBatch(
585+
entries: Array<{
586+
clawId: string;
587+
cycleNumber: number;
588+
toolName: string;
589+
toolArgs: Record<string, unknown>;
590+
toolResult: string;
591+
success: boolean;
592+
durationMs: number;
593+
category?: string;
594+
}>
595+
): Promise<void> {
595596
if (entries.length === 0) return;
596597

597598
// Single batch INSERT instead of N serial queries
@@ -600,7 +601,9 @@ export class ClawsRepository extends BaseRepository {
600601
let idx = 1;
601602

602603
for (const entry of entries) {
603-
valuePlaceholders.push(`($${idx++}, $${idx++}, $${idx++}, $${idx++}, $${idx++}, $${idx++}, $${idx++}, $${idx++}, $${idx++})`);
604+
valuePlaceholders.push(
605+
`($${idx++}, $${idx++}, $${idx++}, $${idx++}, $${idx++}, $${idx++}, $${idx++}, $${idx++}, $${idx++})`
606+
);
604607
params.push(
605608
generateId('aud'),
606609
entry.clawId,
@@ -626,18 +629,21 @@ export class ClawsRepository extends BaseRepository {
626629
limit: number,
627630
offset: number,
628631
category?: string
629-
): Promise<{ entries: Array<{
630-
id: string;
631-
clawId: string;
632-
cycleNumber: number;
633-
toolName: string;
634-
toolArgs: Record<string, unknown>;
635-
toolResult: string;
636-
success: boolean;
637-
durationMs: number;
638-
category: string;
639-
executedAt: string;
640-
}>; total: number }> {
632+
): Promise<{
633+
entries: Array<{
634+
id: string;
635+
clawId: string;
636+
cycleNumber: number;
637+
toolName: string;
638+
toolArgs: Record<string, unknown>;
639+
toolResult: string;
640+
success: boolean;
641+
durationMs: number;
642+
category: string;
643+
executedAt: string;
644+
}>;
645+
total: number;
646+
}> {
641647
let countSql = `SELECT COUNT(*) as count FROM claw_audit_log WHERE claw_id = $1`;
642648
let sql = `SELECT * FROM claw_audit_log WHERE claw_id = $1`;
643649
const params: unknown[] = [clawId];
@@ -655,9 +661,16 @@ export class ClawsRepository extends BaseRepository {
655661

656662
sql += ` ORDER BY executed_at DESC LIMIT $${paramIdx++} OFFSET $${paramIdx}`;
657663
const rows = await this.query<{
658-
id: string; claw_id: string; cycle_number: number; tool_name: string;
659-
tool_args: string; tool_result: string; success: boolean; duration_ms: number;
660-
category: string; executed_at: string;
664+
id: string;
665+
claw_id: string;
666+
cycle_number: number;
667+
tool_name: string;
668+
tool_args: string;
669+
tool_result: string;
670+
success: boolean;
671+
duration_ms: number;
672+
category: string;
673+
executed_at: string;
661674
}>(sql, [...params, limit, offset]);
662675

663676
return {
@@ -679,14 +692,42 @@ export class ClawsRepository extends BaseRepository {
679692

680693
private categorizeToolCall(toolName: string): string {
681694
if (toolName.startsWith('claw_')) return 'claw';
682-
if (toolName.startsWith('browse_') || toolName === 'browser_click' || toolName === 'browser_type' || toolName === 'browser_fill_form' || toolName === 'browser_screenshot' || toolName === 'browser_extract') return 'browser';
683-
if (toolName === 'run_cli_tool' || toolName === 'install_cli_tool' || toolName === 'list_cli_tools') return 'cli';
695+
if (
696+
toolName.startsWith('browse_') ||
697+
toolName === 'browser_click' ||
698+
toolName === 'browser_type' ||
699+
toolName === 'browser_fill_form' ||
700+
toolName === 'browser_screenshot' ||
701+
toolName === 'browser_extract'
702+
)
703+
return 'browser';
704+
if (
705+
toolName === 'run_cli_tool' ||
706+
toolName === 'install_cli_tool' ||
707+
toolName === 'list_cli_tools'
708+
)
709+
return 'cli';
684710
if (toolName === 'run_coding_task' || toolName.startsWith('orchestrat')) return 'coding-agent';
685-
if (toolName.startsWith('fetch_') || toolName.startsWith('http_') || toolName === 'search_web' || toolName === 'post_json' || toolName === 'call_json_api') return 'web';
686-
if (toolName.startsWith('execute_') || toolName === 'eval_js' || toolName === 'eval_python') return 'code-exec';
711+
if (
712+
toolName.startsWith('fetch_') ||
713+
toolName.startsWith('http_') ||
714+
toolName === 'search_web' ||
715+
toolName === 'post_json' ||
716+
toolName === 'call_json_api'
717+
)
718+
return 'web';
719+
if (toolName.startsWith('execute_') || toolName === 'eval_js' || toolName === 'eval_python')
720+
return 'code-exec';
687721
if (toolName.startsWith('git_')) return 'git';
688-
if (toolName.includes('memory') || toolName.includes('goal') || toolName.includes('plan')) return 'knowledge';
689-
if (toolName.startsWith('read_file') || toolName.startsWith('write_file') || toolName.startsWith('list_dir') || toolName.startsWith('delete_file')) return 'filesystem';
722+
if (toolName.includes('memory') || toolName.includes('goal') || toolName.includes('plan'))
723+
return 'knowledge';
724+
if (
725+
toolName.startsWith('read_file') ||
726+
toolName.startsWith('write_file') ||
727+
toolName.startsWith('list_dir') ||
728+
toolName.startsWith('delete_file')
729+
)
730+
return 'filesystem';
690731
return 'tool';
691732
}
692733
}

β€Žpackages/gateway/src/routes/channels-groups.test.tsβ€Ž

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,17 @@ import { requestId } from '../middleware/request-id.js';
1414
import { errorHandler } from '../middleware/error-handler.js';
1515

1616
// Mock Channel Service
17-
const { mockGetChannel, mockGetStatus, mockListGroups, mockGetGroup, mockFetchGroupHistory } = vi.hoisted(() => ({
18-
mockGetChannel: vi.fn(),
19-
mockGetStatus: vi.fn(),
20-
mockListGroups: vi.fn(),
21-
mockGetGroup: vi.fn(),
22-
mockFetchGroupHistory: vi.fn(),
23-
}));
17+
const { mockGetChannel, mockGetStatus, mockListGroups, mockGetGroup, mockFetchGroupHistory } =
18+
vi.hoisted(() => ({
19+
mockGetChannel: vi.fn(),
20+
mockGetStatus: vi.fn(),
21+
mockListGroups: vi.fn(),
22+
mockGetGroup: vi.fn(),
23+
mockFetchGroupHistory: vi.fn(),
24+
}));
2425

2526
vi.mock('@ownpilot/core', async (importOriginal) => ({
26-
...await importOriginal<Record<string, unknown>>(),
27+
...(await importOriginal<Record<string, unknown>>()),
2728
getChannelService: vi.fn().mockReturnValue({
2829
getChannel: mockGetChannel,
2930
}),

β€Žpackages/gateway/src/routes/claws.test.tsβ€Ž

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,7 @@ describe('Claws Routes', () => {
8888

8989
describe('GET /claws', () => {
9090
it('should return list of claws', async () => {
91-
service.listClaws.mockResolvedValue([
92-
{ id: 'claw-1', name: 'Test', mode: 'continuous' },
93-
]);
91+
service.listClaws.mockResolvedValue([{ id: 'claw-1', name: 'Test', mode: 'continuous' }]);
9492
service.listSessions.mockReturnValue([]);
9593

9694
const res = await app.request('/claws');

0 commit comments

Comments
Β (0)