diff --git a/package-lock.json b/package-lock.json index 872cfd700d..542c13d80a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -386,9 +386,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -406,9 +403,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -426,9 +420,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -446,9 +437,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1328,9 +1316,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -1348,9 +1333,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -1368,9 +1350,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -1388,9 +1367,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -1476,9 +1452,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1493,9 +1466,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1510,9 +1480,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1527,9 +1494,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2747,9 +2711,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2767,9 +2728,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2787,9 +2745,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2807,9 +2762,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2827,9 +2779,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2847,9 +2796,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2867,9 +2813,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2887,9 +2830,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2907,9 +2847,6 @@ "arm" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2933,9 +2870,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2959,9 +2893,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -2985,9 +2916,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3011,9 +2939,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3037,9 +2962,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3063,9 +2985,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -3089,9 +3008,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -11502,9 +11418,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -11522,9 +11435,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -11542,9 +11452,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -11562,9 +11469,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -11582,9 +11486,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -11602,9 +11503,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -11622,9 +11520,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -11642,9 +11537,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ diff --git a/packages/runtime-host/src/__tests__/execution-model-composition.test.ts b/packages/runtime-host/src/__tests__/execution-model-composition.test.ts index bfae661190..918fcc4de2 100644 --- a/packages/runtime-host/src/__tests__/execution-model-composition.test.ts +++ b/packages/runtime-host/src/__tests__/execution-model-composition.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { deferred } from '@maka/core/test-only/async-primitives'; +import { deferred, waitFor } from '@maka/core/test-only/async-primitives'; import assert from 'node:assert/strict'; import { execFile } from 'node:child_process'; import { randomUUID } from 'node:crypto'; @@ -2207,25 +2207,30 @@ test('production Host executes and durably supervises an Agent Graph over a real assert.equal(initialTerminal.status, 'completed'); graphStore = createAgentGraphControlStore(root); + const graph = graphStore; const graphId = agentGraphIdForRootSession(session.id); - let updates = await graphStore.listAgentGraphScheduleUpdates(graphId); + let updates = await graph.listAgentGraphScheduleUpdates(graphId); let runs = await execution.runtimeEventStore.listSessionInvocations(session.id); - for (let attempt = 0; attempt < 400; attempt += 1) { - const wakeRuns = runs.filter( - (run) => run.opening.root.kind === 'agent_graph_supervisor_wake', - ); - if ( - updates.at(-1)?.finish && - wakeRuns.length > 0 && - wakeRuns.every((run) => runtimeInvocationOutcome(run) !== undefined) && - liveResidencies === 0 - ) { - break; - } - await new Promise((resolve) => setTimeout(resolve, 10)); - updates = await graphStore.listAgentGraphScheduleUpdates(graphId); - runs = await execution.runtimeEventStore.listSessionInvocations(session.id); - } + await waitFor( + async () => { + updates = await graph.listAgentGraphScheduleUpdates(graphId); + runs = await execution.runtimeEventStore.listSessionInvocations(session.id); + const wakeRuns = runs.filter( + (run) => run.opening.root.kind === 'agent_graph_supervisor_wake', + ); + return Boolean( + updates.at(-1)?.finish && + wakeRuns.length > 0 && + wakeRuns.every((run) => runtimeInvocationOutcome(run) !== undefined) && + liveResidencies === 0, + ); + }, + { + timeoutMs: 5_000, + pollMs: 10, + message: 'Hosted Graph wake runs did not settle', + }, + ); const finish = updates.at(-1)?.finish; assert.ok( @@ -3799,19 +3804,33 @@ async function startTurn( text: string, context: ConnectionContext, ): Promise { - for (let attempt = 0; attempt < 200; attempt += 1) { - const input = { sessionId, turnId, content: { text } }; - const started = await composition.handlers['turn.start'](input, context); - if (started.ok) { - if (started.result.kind === 'started') return started.result.turn; - throw new Error(`Hosted real-model Skill invocation was blocked: ${JSON.stringify(started)}`); - } - if (started.error.code !== 'session_busy') { - throw new Error(`Hosted real-model Turn start failed: ${JSON.stringify(started.error)}`); - } - await new Promise((resolve) => setTimeout(resolve, 10)); - } - throw new Error('Hosted real-model Session did not become idle'); + let turn: TurnSnapshot | undefined; + const input = { sessionId, turnId, content: { text } }; + await waitFor( + async () => { + const started = await composition.handlers['turn.start'](input, context); + if (started.ok) { + if (started.result.kind === 'started') { + turn = started.result.turn; + return true; + } + throw new Error( + `Hosted real-model Skill invocation was blocked: ${JSON.stringify(started)}`, + ); + } + if (started.error.code !== 'session_busy') { + throw new Error(`Hosted real-model Turn start failed: ${JSON.stringify(started.error)}`); + } + return false; + }, + { + timeoutMs: 5_000, + pollMs: 10, + message: 'Hosted real-model Session did not become idle', + }, + ); + assert.ok(turn); + return turn; } async function waitForTerminal( @@ -3822,14 +3841,21 @@ async function waitForTerminal( context: ConnectionContext, ): Promise { let snapshot = initial; - for (let attempt = 0; attempt < 200; attempt += 1) { - if (isTerminal(snapshot)) return snapshot; - await new Promise((resolve) => setTimeout(resolve, 10)); - const queried = await composition.handlers['turn.query']({ sessionId, turnId }, context); - assert.equal(queried.ok, true); - snapshot = queried.result; - } - throw new Error('Hosted real-model Turn did not become terminal'); + await waitFor( + async () => { + if (isTerminal(snapshot)) return true; + const queried = await composition.handlers['turn.query']({ sessionId, turnId }, context); + assert.equal(queried.ok, true); + snapshot = queried.result; + return false; + }, + { + timeoutMs: 5_000, + pollMs: 10, + message: 'Hosted real-model Turn did not become terminal', + }, + ); + return snapshot; } async function waitForUsage( @@ -3838,23 +3864,35 @@ async function waitForUsage( connectionSlug: string, callKind: ModelCallKind, ): Promise['rows'][number]> { - for (let attempt = 0; attempt < 100; attempt += 1) { - const queried = await composition.handlers['usage.query']( - { kind: 'logs', source: 'llm', query: { range: 'all' } }, - context, - ); - assert.equal(queried.ok, true); - if (queried.result.kind === 'logs' && queried.result.source === 'llm') { - const row = queried.result.rows.find( - (candidate) => - candidate.connectionSlug === connectionSlug && - (candidate.callKind ?? 'main') === callKind, + let row: Extract['rows'][number] | undefined; + await waitFor( + async () => { + const queried = await composition.handlers['usage.query']( + { kind: 'logs', source: 'llm', query: { range: 'all' } }, + context, ); - if (row) return row; - } - await new Promise((resolve) => setTimeout(resolve, 10)); - } - throw new Error('Hosted real-model usage attribution was not persisted'); + assert.equal(queried.ok, true); + if (queried.result.kind === 'logs' && queried.result.source === 'llm') { + const found = queried.result.rows.find( + (candidate) => + candidate.connectionSlug === connectionSlug && + (candidate.callKind ?? 'main') === callKind, + ); + if (found) { + row = found; + return true; + } + } + return false; + }, + { + timeoutMs: 5_000, + pollMs: 10, + message: 'Hosted real-model usage attribution was not persisted', + }, + ); + assert.ok(row); + return row; } async function waitForCanonicalRequests( @@ -3863,19 +3901,35 @@ async function waitForCanonicalRequests( expectedRequests: number, ): Promise { const ask = () => usage.modelCalls.modelCallSummary({ range: 'all', sessionId }, Date.now()); - for (let attempt = 0; attempt < 100; attempt += 1) { - const { projection } = await ask(); - if (projection.totalRequests >= expectedRequests) return projection.totalRequests; - await new Promise((resolve) => setTimeout(resolve, 10)); + let totalRequests: number | undefined; + try { + await waitFor( + async () => { + const { projection } = await ask(); + if (projection.totalRequests >= expectedRequests) { + totalRequests = projection.totalRequests; + return true; + } + return false; + }, + { + timeoutMs: 5_000, + pollMs: 10, + message: 'Hosted canonical model-call attempts were not persisted', + }, + ); + } catch { + const { projection, unreadableRecords } = await ask(); + throw new Error( + `Hosted canonical model-call attempts were not persisted: ${JSON.stringify({ + expectedRequests, + totalRequests: projection.totalRequests, + unreadableRecords, + })}`, + ); } - const { projection, unreadableRecords } = await ask(); - throw new Error( - `Hosted canonical model-call attempts were not persisted: ${JSON.stringify({ - expectedRequests, - totalRequests: projection.totalRequests, - unreadableRecords, - })}`, - ); + assert.ok(totalRequests !== undefined); + return totalRequests; } async function waitForAutomaticMemoryRequestsToSettle( @@ -3883,27 +3937,38 @@ async function waitForAutomaticMemoryRequestsToSettle( ): Promise { let stablePolls = 0; let previousCount = -1; - for (let attempt = 0; attempt < 100; attempt += 1) { - const memoryCount = requests.filter((request) => - /Perform the first stage of long-term-memory extraction/.test(JSON.stringify(request.body)), - ).length; - if (memoryCount > 0 && requests.length === previousCount) stablePolls += 1; - else stablePolls = 0; - if (stablePolls >= 5) return; - previousCount = requests.length; - await new Promise((resolve) => setTimeout(resolve, 10)); + try { + await waitFor( + () => { + const memoryCount = requests.filter((request) => + /Perform the first stage of long-term-memory extraction/.test( + JSON.stringify(request.body), + ), + ).length; + if (memoryCount > 0 && requests.length === previousCount) stablePolls += 1; + else stablePolls = 0; + previousCount = requests.length; + return stablePolls >= 5; + }, + { + timeoutMs: 5_000, + pollMs: 10, + message: 'Hosted automatic Memory extraction request did not settle', + }, + ); + } catch { + throw new Error( + `Hosted automatic Memory extraction request did not settle: ${JSON.stringify( + requests.map((request) => ({ + stream: request.body.stream, + summary: /context summarization assistant/.test(JSON.stringify(request.body)), + memory: /Perform the first stage of long-term-memory extraction/.test( + JSON.stringify(request.body), + ), + })), + )}`, + ); } - throw new Error( - `Hosted automatic Memory extraction request did not settle: ${JSON.stringify( - requests.map((request) => ({ - stream: request.body.stream, - summary: /context summarization assistant/.test(JSON.stringify(request.body)), - memory: /Perform the first stage of long-term-memory extraction/.test( - JSON.stringify(request.body), - ), - })), - )}`, - ); } function isTerminal(snapshot: TurnSnapshot): boolean { diff --git a/packages/runtime-host/src/__tests__/peer-mesh.test.ts b/packages/runtime-host/src/__tests__/peer-mesh.test.ts index 73355369a4..aa5a66c301 100644 --- a/packages/runtime-host/src/__tests__/peer-mesh.test.ts +++ b/packages/runtime-host/src/__tests__/peer-mesh.test.ts @@ -23,6 +23,7 @@ import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { setImmediate as waitForImmediate, setTimeout as delay } from 'node:timers/promises'; +import { waitFor } from '@maka/core/test-only/async-primitives'; import { test } from 'node:test'; import type { RuntimeHostPeerNativeStream } from '../transport/peer-native.js'; import { @@ -280,11 +281,11 @@ test('announces authority commits without coupling success to delivery', async ( await member.join(await authority.invite(mesh.roster.roster.meshId)); await authority.setMeshDisplayName(mesh.roster.roster.meshId, 'Online'); - for (let attempt = 0; attempt < 20; attempt += 1) { - if (member.status()[0]?.roster.roster.displayName === 'Online') break; - await delay(10); - } - assert.equal(member.status()[0]?.roster.roster.displayName, 'Online'); + await waitFor(() => member.status()[0]?.roster.roster.displayName === 'Online', { + timeoutMs: 5_000, + pollMs: 10, + message: 'Mesh display name did not propagate to the member', + }); memberPeer.stallNextControl(); await authority.setMeshDisplayName(mesh.roster.roster.meshId, 'Recovered'); diff --git a/packages/runtime-host/src/__tests__/peer-native.test.ts b/packages/runtime-host/src/__tests__/peer-native.test.ts index 52ce806e79..d4b0acae12 100644 --- a/packages/runtime-host/src/__tests__/peer-native.test.ts +++ b/packages/runtime-host/src/__tests__/peer-native.test.ts @@ -22,6 +22,7 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join, relative } from 'node:path'; import { setImmediate as waitForImmediate } from 'node:timers/promises'; +import { waitFor } from '@maka/core/test-only/async-primitives'; import { test } from 'node:test'; import { createRuntimeHostPeerClient, @@ -515,9 +516,11 @@ async function waitForRequestCount( stats: { readonly requests: readonly unknown[] }, expected: number, ): Promise { - for (let attempt = 0; attempt < 10 && stats.requests.length < expected; attempt += 1) { - await waitForImmediate(); - } + await waitFor(() => stats.requests.length >= expected, { + timeoutMs: 5_000, + pollMs: 10, + message: `Peer did not receive ${expected} request(s)`, + }); assert.equal(stats.requests.length, expected); } diff --git a/packages/runtime-host/src/__tests__/plan-two-client-uds.test.ts b/packages/runtime-host/src/__tests__/plan-two-client-uds.test.ts index e1febb5e9b..92aa694825 100644 --- a/packages/runtime-host/src/__tests__/plan-two-client-uds.test.ts +++ b/packages/runtime-host/src/__tests__/plan-two-client-uds.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { withTimeout } from '@maka/core/test-only/async-primitives'; +import { waitFor, withTimeout } from '@maka/core/test-only/async-primitives'; import { defineInteractiveRuntimeHostComposition } from '../server/host-composition.js'; import assert from 'node:assert/strict'; import { mkdtemp, rm } from 'node:fs/promises'; @@ -225,21 +225,27 @@ async function waitForTerminal( initial: OperationOutput<'plan.turn.start'>['turn'], ): Promise { let snapshot = initial; - for (let attempt = 0; attempt < 100; attempt += 1) { - if ( - snapshot.status === 'completed' || - snapshot.status === 'failed' || - snapshot.status === 'cancelled' - ) { - return; - } - await new Promise((resolve) => setTimeout(resolve, 10)); - snapshot = await connection.request('turn.query', { - sessionId: snapshot.sessionId, - turnId: snapshot.turnId, - }); - } - throw new Error('Plan execution Turn did not settle'); + await waitFor( + async () => { + if ( + snapshot.status === 'completed' || + snapshot.status === 'failed' || + snapshot.status === 'cancelled' + ) { + return true; + } + snapshot = await connection.request('turn.query', { + sessionId: snapshot.sessionId, + turnId: snapshot.turnId, + }); + return false; + }, + { + timeoutMs: 5_000, + pollMs: 10, + message: 'Plan execution Turn did not settle', + }, + ); } async function nextFrameOfKind( diff --git a/packages/runtime-host/src/__tests__/runtime-policy-coordinator.test.ts b/packages/runtime-host/src/__tests__/runtime-policy-coordinator.test.ts index 84231b07ea..1dd4d87efc 100644 --- a/packages/runtime-host/src/__tests__/runtime-policy-coordinator.test.ts +++ b/packages/runtime-host/src/__tests__/runtime-policy-coordinator.test.ts @@ -369,16 +369,26 @@ test('production policy mutation drains and poisons activation when cached backe assert.equal(started.result.kind, 'started'); if (started.result.kind !== 'started') return; let snapshot = started.result.turn; - for (let attempt = 0; attempt < 100 && !isTerminalTurnStatus(snapshot.status); attempt += 1) { - await new Promise((resolve) => setTimeout(resolve, 20)); - const queried = await composition.handlers['turn.query']( - { sessionId: session.id, turnId: firstTurnId }, - context, - ); - assert.equal(queried.ok, true); - if (!queried.ok) return; - snapshot = queried.result; - } + const activeComposition = composition; + assert.ok(activeComposition); + await pollFor( + async () => { + if (isTerminalTurnStatus(snapshot.status)) return true; + const queried = await activeComposition.handlers['turn.query']( + { sessionId: session.id, turnId: firstTurnId }, + context, + ); + assert.equal(queried.ok, true); + if (!queried.ok) return true; + snapshot = queried.result; + return false; + }, + { + timeoutMs: 5_000, + pollMs: 10, + message: 'Cached-backend Turn did not reach a terminal status', + }, + ); assert.equal(isTerminalTurnStatus(snapshot.status), true); disposalSpy = mock.method(FakeBackend.prototype, 'dispose', async () => {