diff --git a/docs/reviews/sentrux/2026-04-04-022609-codex-agentos-tsc-cleanup.md b/docs/reviews/sentrux/2026-04-04-022609-codex-agentos-tsc-cleanup.md new file mode 100644 index 0000000..c6101c9 --- /dev/null +++ b/docs/reviews/sentrux/2026-04-04-022609-codex-agentos-tsc-cleanup.md @@ -0,0 +1,37 @@ +# Sentrux Review Report + +- Actor: `codex` +- Scope: `agentos-tsc-cleanup` +- Timestamp: `2026-04-04T02:26:09+07:00` +- Worktree: `/home/wunitb/unitb_labs/wunb-agentos-beeos/.worktrees/agentos-beeos-main-runtime` +- Base commit: `4b4de07e2603d71f87bee17a91a8185dcb82e6a7` + +## Commands Used + +```text +npx tsc -p tsconfig.json --noEmit --pretty false +npm test -- src/__tests__/mcp-client.test.ts src/__tests__/security-headers.test.ts src/__tests__/skillkit-bridge.test.ts src/__tests__/streaming.test.ts src/__tests__/llm-router.test.ts src/__tests__/session-lifecycle.test.ts src/__tests__/cron.test.ts src/__tests__/api.test.ts +git diff --check +Sentrux scan: /home/wunitb/unitb_labs/wunb-agentos-beeos/.worktrees/agentos-beeos-main-runtime +Sentrux check_rules +``` + +## Findings + +- Severity: medium +- Area: TypeScript compile hygiene and iii-sdk contract drift +- Evidence: `tsc` initially failed on test mock signatures, `request_format`/`response_format` shape in `src/api.ts`, `unknown` state-list returns in `src/context-cache.ts`, `src/cron.ts`, and `src/session-lifecycle.ts`, plus drift around `agentTier`, `function_id`, and `globalThis.vitest`. +- Recommended change: align local typings with current iii-sdk schema contract, keep request/response schemas wrapped in a top-level object format node, and prefer explicit narrowing/casts at `state::*` boundaries instead of relying on `any`. + +## Handoff For Dev Session + +- Files likely affected: + `src/api.ts`, `src/llm-router.ts`, `src/types.ts`, `src/context-cache.ts`, `src/cron.ts`, `src/session-lifecycle.ts`, `src/shared/metrics.ts`, `src/evolve.ts`, targeted test files. +- First fix to attempt: + Re-run `tsc` first whenever `iii-sdk` is upgraded, because compile errors clearly expose contract drift before runtime. +- Verification expected after fix: + `npx tsc -p tsconfig.json --noEmit --pretty false` exits 0 and the targeted vitest slice above stays green. + +## MegaMemory Update + +- Created concept: `agentos-runtime-typescript-contract-cleanup` diff --git a/docs/reviews/sentrux/2026-04-04-204700-codex-agentos-runtime-latest-main-sync.md b/docs/reviews/sentrux/2026-04-04-204700-codex-agentos-runtime-latest-main-sync.md new file mode 100644 index 0000000..269451d --- /dev/null +++ b/docs/reviews/sentrux/2026-04-04-204700-codex-agentos-runtime-latest-main-sync.md @@ -0,0 +1,32 @@ +# Sentrux Review Report + +- Actor: `codex` +- Scope: `agentos-runtime-latest-main-sync` +- Timestamp: `2026-04-04T20:47:00+07:00` +- Worktree: `/home/wunitb/unitb_labs/wunb-agentos-beeos/.worktrees/agentos-beeos-main-runtime` + +## Commands Used + +```text +git fetch --all --prune +git switch --detach origin/main +git cherry-pick -n 9179d7f9410d4ed8b44e25c90400e2db9479502e +npx tsc -p tsconfig.json --noEmit --pretty false +npm test -- src/__tests__/mcp-client.test.ts src/__tests__/security-headers.test.ts src/__tests__/skillkit-bridge.test.ts src/__tests__/streaming.test.ts src/__tests__/llm-router.test.ts src/__tests__/session-lifecycle.test.ts src/__tests__/cron.test.ts src/__tests__/api.test.ts +git diff --check +sentrux scan /home/wunitb/unitb_labs/wunb-agentos-beeos/.worktrees/agentos-beeos-main-runtime +``` + +## Findings + +- Upstream `origin/main` advanced to `c664876` but was not runtime-clean for this BeeOS baseline. +- Reapplying the previous runtime TypeScript cleanup onto latest main resolved the drift with two extra surgical fixes in `src/llm-router.ts` and `src/memory.ts`. +- Fresh verification passed: targeted tests `152/152`, `tsc` clean, and `git diff --check` clean. +- Sentrux scan reported `quality_signal 6112`. +- No local `.sentrux/rules.toml` exists in this repo/worktree, so rule-gate verification is not available here. + +## Handoff + +- Runtime worktree branch: `beeos-main-runtime-latest` +- Baseline commit after local sync should be used by `beeos-agentos.service` +- `node_modules` remains an intentional untracked symlink runtime asset diff --git a/src/__tests__/mcp-client.test.ts b/src/__tests__/mcp-client.test.ts index 473bbd5..967a114 100644 --- a/src/__tests__/mcp-client.test.ts +++ b/src/__tests__/mcp-client.test.ts @@ -1,7 +1,11 @@ import { describe, it, expect, vi, beforeEach, beforeAll } from "vitest"; -const mockTrigger = vi.fn(async () => null); -const mockTriggerVoid = vi.fn(); +const mockTrigger = vi.fn( + async (_fnId?: string, _payload?: unknown): Promise => null, +); +const mockTriggerVoid = vi.fn( + (_fnId?: string, _payload?: unknown): void => undefined, +); const handlers: Record = {}; const triggerRefs: any[] = []; diff --git a/src/__tests__/security-headers.test.ts b/src/__tests__/security-headers.test.ts index 5ec4faa..499682f 100644 --- a/src/__tests__/security-headers.test.ts +++ b/src/__tests__/security-headers.test.ts @@ -1,8 +1,12 @@ import { describe, it, expect, vi, beforeAll } from "vitest"; const handlers: Record = {}; -const mockTrigger = vi.fn(async () => null); -const mockTriggerVoid = vi.fn(); +const mockTrigger = vi.fn( + async (_fnId?: string, _payload?: unknown): Promise => null, +); +const mockTriggerVoid = vi.fn( + (_fnId?: string, _payload?: unknown): void => undefined, +); vi.mock("iii-sdk", () => ({ registerWorker: () => ({ registerFunction: (config: any, handler: Function) => { handlers[config.id] = handler; }, diff --git a/src/__tests__/skillkit-bridge.test.ts b/src/__tests__/skillkit-bridge.test.ts index fe96439..73305db 100644 --- a/src/__tests__/skillkit-bridge.test.ts +++ b/src/__tests__/skillkit-bridge.test.ts @@ -1,8 +1,12 @@ import { describe, it, expect, vi, beforeEach, beforeAll } from "vitest"; const handlers: Record = {}; -const mockTrigger = vi.fn(async () => null); -const mockTriggerVoid = vi.fn(); +const mockTrigger = vi.fn( + async (_fnId?: string, _payload?: unknown): Promise => null, +); +const mockTriggerVoid = vi.fn( + (_fnId?: string, _payload?: unknown): void => undefined, +); vi.mock("iii-sdk", () => ({ registerWorker: () => ({ registerFunction: (config: any, handler: Function) => { diff --git a/src/__tests__/streaming.test.ts b/src/__tests__/streaming.test.ts index 4c6b9f3..f6769ac 100644 --- a/src/__tests__/streaming.test.ts +++ b/src/__tests__/streaming.test.ts @@ -21,6 +21,9 @@ const mockTrigger = vi.fn(async (fnId: string, data?: any): Promise => { }; return null; }); +const mockTriggerVoid = vi.fn( + (_fnId?: string, _payload?: unknown): void => undefined, +); const handlers: Record = {}; vi.mock("iii-sdk", () => ({ diff --git a/src/api.ts b/src/api.ts index cd74268..e8dbc3c 100644 --- a/src/api.ts +++ b/src/api.ts @@ -73,60 +73,68 @@ registerFunction( id: "api::chat_completions", description: "OpenAI-compatible chat completions", metadata: { category: "api" }, - request_format: [ - { name: "model", type: "string", required: true, description: "Model identifier" }, - { - name: "messages", - type: "array", - required: true, - description: "Array of chat messages", - items: { - name: "message", - type: "object", - body: [ - { name: "role", type: "string", required: true }, - { name: "content", type: "string", required: true }, - ], + request_format: { + name: "chat_completions_request", + type: "object", + body: [ + { name: "model", type: "string", required: true, description: "Model identifier" }, + { + name: "messages", + type: "array", + required: true, + description: "Array of chat messages", + items: { + name: "message", + type: "object", + body: [ + { name: "role", type: "string", required: true }, + { name: "content", type: "string", required: true }, + ], + }, }, - }, - ], - response_format: [ - { name: "id", type: "string", description: "Completion ID" }, - { name: "object", type: "string", description: "Object type" }, - { name: "created", type: "number", description: "Unix timestamp" }, - { name: "model", type: "string", description: "Model used" }, - { - name: "choices", - type: "array", - description: "Completion choices", - items: { - name: "choice", + ], + }, + response_format: { + name: "chat_completions_response", + type: "object", + body: [ + { name: "id", type: "string", description: "Completion ID" }, + { name: "object", type: "string", description: "Object type" }, + { name: "created", type: "number", description: "Unix timestamp" }, + { name: "model", type: "string", description: "Model used" }, + { + name: "choices", + type: "array", + description: "Completion choices", + items: { + name: "choice", + type: "object", + body: [ + { name: "index", type: "number" }, + { + name: "message", + type: "object", + body: [ + { name: "role", type: "string" }, + { name: "content", type: "string" }, + ], + }, + { name: "finish_reason", type: "string" }, + ], + }, + }, + { + name: "usage", type: "object", + description: "Token usage", body: [ - { name: "index", type: "number" }, - { - name: "message", - type: "object", - body: [ - { name: "role", type: "string" }, - { name: "content", type: "string" }, - ], - }, - { name: "finish_reason", type: "string" }, + { name: "prompt_tokens", type: "number" }, + { name: "completion_tokens", type: "number" }, + { name: "total_tokens", type: "number" }, ], }, - }, - { - name: "usage", - type: "object", - description: "Token usage", - body: [ - { name: "prompt_tokens", type: "number" }, - { name: "completion_tokens", type: "number" }, - { name: "total_tokens", type: "number" }, - ], - }, - ], + ], + }, }, async (req) => { const authErr = authGuard(req); @@ -175,24 +183,32 @@ registerFunction( id: "api::agent_message", description: "Send message to a specific agent", metadata: { category: "api" }, - request_format: [ - { name: "message", type: "string", required: true, description: "Message to send" }, - { name: "sessionId", type: "string", required: false, description: "Optional session ID" }, - ], - response_format: [ - { name: "content", type: "string", description: "Agent response content" }, - { name: "model", type: "string", description: "Model used" }, - { - name: "usage", - type: "object", - description: "Token usage", - body: [ - { name: "input", type: "number" }, - { name: "output", type: "number" }, - { name: "total", type: "number" }, - ], - }, - ], + request_format: { + name: "agent_message_request", + type: "object", + body: [ + { name: "message", type: "string", required: true, description: "Message to send" }, + { name: "sessionId", type: "string", required: false, description: "Optional session ID" }, + ], + }, + response_format: { + name: "agent_message_response", + type: "object", + body: [ + { name: "content", type: "string", description: "Agent response content" }, + { name: "model", type: "string", description: "Model used" }, + { + name: "usage", + type: "object", + description: "Token usage", + body: [ + { name: "input", type: "number" }, + { name: "output", type: "number" }, + { name: "total", type: "number" }, + ], + }, + ], + }, }, async (req) => { const authErr = authGuard(req); @@ -234,16 +250,24 @@ registerFunction( id: "api::create_agent", description: "Create a new agent", metadata: { category: "api" }, - request_format: [ - { name: "name", type: "string", required: true, description: "Agent name" }, - { name: "systemPrompt", type: "string", required: true, description: "System prompt for the agent" }, - { name: "model", type: "string", required: true, description: "LLM model identifier" }, - ], - response_format: [ - { name: "id", type: "string", description: "Created agent ID" }, - { name: "name", type: "string", description: "Agent name" }, - { name: "createdAt", type: "number", description: "Creation timestamp" }, - ], + request_format: { + name: "create_agent_request", + type: "object", + body: [ + { name: "name", type: "string", required: true, description: "Agent name" }, + { name: "systemPrompt", type: "string", required: true, description: "System prompt for the agent" }, + { name: "model", type: "string", required: true, description: "LLM model identifier" }, + ], + }, + response_format: { + name: "create_agent_response", + type: "object", + body: [ + { name: "id", type: "string", description: "Created agent ID" }, + { name: "name", type: "string", description: "Agent name" }, + { name: "createdAt", type: "number", description: "Creation timestamp" }, + ], + }, }, async (req) => { const authErr = authGuard(req); @@ -332,12 +356,16 @@ registerFunction( id: "api::health", description: "Health check endpoint", metadata: { category: "api" }, - response_format: [ - { name: "status", type: "string", description: "Health status" }, - { name: "version", type: "string", description: "Application version" }, - { name: "workers", type: "number", description: "Active worker count" }, - { name: "uptime", type: "number", description: "Process uptime in seconds" }, - ], + response_format: { + name: "health_response", + type: "object", + body: [ + { name: "status", type: "string", description: "Health status" }, + { name: "version", type: "string", description: "Application version" }, + { name: "workers", type: "number", description: "Active worker count" }, + { name: "uptime", type: "number", description: "Process uptime in seconds" }, + ], + }, }, async (req: any) => { if (shutdownManager.isShuttingDown()) { diff --git a/src/context-cache.ts b/src/context-cache.ts index b7859cf..d258fba 100644 --- a/src/context-cache.ts +++ b/src/context-cache.ts @@ -107,10 +107,10 @@ registerFunction( return httpOk(req, { cleared: 1 }); } - const entries: any[] = await trigger({ + const entries = (await trigger({ function_id: "state::list", payload: { scope }, - }).catch(() => []); + }).catch(() => [])) as any[]; let cleared = 0; for (const entry of entries || []) { diff --git a/src/cron.ts b/src/cron.ts index 28b438e..732a8fa 100644 --- a/src/cron.ts +++ b/src/cron.ts @@ -15,10 +15,10 @@ registerFunction( metadata: { category: "cron" }, }, async () => { - const agents: any[] = await trigger({ + const agents = (await trigger({ function_id: "state::list", payload: { scope: "agents" }, - }).catch(() => []); + }).catch(() => [])) as any[]; const cutoff = Date.now() - 24 * 60 * 60 * 1000; let cleaned = 0; @@ -26,10 +26,10 @@ registerFunction( const agentId = agent.key || agent.id; if (!agentId) continue; - const sessions: any[] = await trigger({ + const sessions = (await trigger({ function_id: "state::list", payload: { scope: `sessions:${agentId}` }, - }).catch(() => []); + }).catch(() => [])) as any[]; for (const session of sessions) { const lastActive = session.value?.lastActiveAt || session.value?.createdAt || 0; @@ -61,10 +61,10 @@ registerFunction( }).catch(() => null); if (costs) { - const metering: any[] = await trigger({ + const metering = (await trigger({ function_id: "state::list", payload: { scope: "metering" }, - }).catch(() => []); + }).catch(() => [])) as any[]; let totalTokens = 0; for (const entry of metering) { @@ -95,10 +95,10 @@ registerFunction( metadata: { category: "cron" }, }, async () => { - const rates: any[] = await trigger({ + const rates = (await trigger({ function_id: "state::list", payload: { scope: "rates" }, - }).catch(() => []); + }).catch(() => [])) as any[]; let reset = 0; for (const rate of rates) { diff --git a/src/evolve.ts b/src/evolve.ts index 79c0f9e..f1f586b 100644 --- a/src/evolve.ts +++ b/src/evolve.ts @@ -760,7 +760,9 @@ async function reloadEvolvedFunctions() { } } -if (process.env.NODE_ENV !== "test" && typeof globalThis.vitest === "undefined") { +const testGlobals = globalThis as typeof globalThis & { vitest?: unknown }; + +if (process.env.NODE_ENV !== "test" && typeof testGlobals.vitest === "undefined") { setTimeout(() => { reloadEvolvedFunctions().catch(() => {}); }, 2000); diff --git a/src/llm-router.ts b/src/llm-router.ts index 61af895..8f08d6a 100644 --- a/src/llm-router.ts +++ b/src/llm-router.ts @@ -20,7 +20,7 @@ interface CompleteRequest { model: ModelSelection; systemPrompt?: string; messages: Array<{ role: string; content: string }>; - tools?: Array<{ id: string; description?: string }>; + tools?: Array<{ id?: string; function_id?: string; description?: string }>; toolResults?: Array<{ toolCallId: string; output: unknown }>; } @@ -218,11 +218,14 @@ registerFunction( async function callAnthropic(req: CompleteRequest) { const client = new Anthropic(); - const tools = req.tools?.map((t) => ({ - name: t.id.replace(/::/g, "_"), - description: t.description || t.id, - input_schema: { type: "object" as const, properties: {} }, - })); + const tools = req.tools?.map((t) => { + const toolId = t.id || t.function_id || "unknown"; + return { + name: toolId.replace(/::/g, "_"), + description: t.description || toolId, + input_schema: { type: "object" as const, properties: {} }, + }; + }); const messages = req.messages .filter((m) => m.role !== "system") @@ -299,14 +302,16 @@ async function callOpenAICompat(req: CompleteRequest, providerId: string) { }; if (req.tools?.length) { - body.tools = req.tools.map((t) => ({ + body.tools = req.tools + .filter((t): t is typeof t & { id: string } => typeof t?.id === "string" && t.id.length > 0) + .map((t) => ({ type: "function", function: { name: t.id.replace(/::/g, "_"), description: t.description || t.id, parameters: { type: "object", properties: {} }, }, - })); + })); } const headers: Record = { diff --git a/src/memory.ts b/src/memory.ts index 1c5f92d..4ed78fe 100644 --- a/src/memory.ts +++ b/src/memory.ts @@ -528,7 +528,7 @@ registerFunction( for (const m of memories) { const sid = m.sessionId!; const content = m.content.toLowerCase(); - const hits = keywords.filter((k) => content.includes(k)).length; + const hits = keywords.filter((keyword: string) => content.includes(keyword)).length; if (hits === 0) continue; const keywordScore = hits / Math.max(keywords.length, 1); diff --git a/src/session-lifecycle.ts b/src/session-lifecycle.ts index 7f49524..0d23fdf 100644 --- a/src/session-lifecycle.ts +++ b/src/session-lifecycle.ts @@ -53,6 +53,11 @@ interface Reaction { attempts: number; } +type StoredReaction = { + key?: string; + value: Reaction; +}; + registerFunction( { id: "lifecycle::transition", @@ -119,7 +124,7 @@ registerFunction( }); const [agentReactions, globalReactions] = await Promise.all([ - safeCall( + safeCall( () => trigger({ function_id: "state::list", @@ -128,7 +133,7 @@ registerFunction( [], { operation: "list_agent_reactions" }, ), - safeCall( + safeCall( () => trigger({ function_id: "state::list", diff --git a/src/shared/metrics.ts b/src/shared/metrics.ts index 1605bfd..d56ebb3 100644 --- a/src/shared/metrics.ts +++ b/src/shared/metrics.ts @@ -6,7 +6,7 @@ export function recordMetric( ) { } -type TriggerVoidFn = (id: string, input: unknown) => void | Promise; +type TriggerVoidFn = (id: string, input: unknown) => void | Promise; export function createRecordMetric(triggerVoid: TriggerVoidFn) { return (name: string, value: number, labels?: Record, _type?: string) => { diff --git a/src/types.ts b/src/types.ts index 46aaa77..d8f1f69 100644 --- a/src/types.ts +++ b/src/types.ts @@ -28,6 +28,7 @@ export interface AgentConfig { id?: string; name: string; description?: string; + agentTier?: "economy" | "premium"; model?: { provider?: string; model?: string;