{"record":{"id":"382555a85cd1c6d6","repo":"Hmbown/CodeWhale","slug":"the-local-runtime-sdk-needs-threadevents-support","errorCode":null,"errorMessage":"The local Runtime SDK needs threadEvents support.","messagePattern":"The local Runtime SDK needs threadEvents support\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/scripts/lib/pet-runtime.mjs","lineNumber":19,"sourceCode":"import { setTimeout as delay } from 'node:timers/promises';\nimport { privacyEvent, redact } from '../../dist/core/ingest.js';\nimport { CodewhaleRuntimeTrace, isCodewhaleRuntimeRecord, observeRuntimeRequests } from '../../dist/core/codewhale.js';\n\n/** A read-only transport for the existing Runtime journal. All event meaning\n * remains in Whalesong's importer and canonical pet bucketer. No raw journal,\n * prompt, tool argument or bearer token is written into the pet recording. */\nexport async function followRuntime({ baseUrl, threadId, token, report = () => {} }) {\n  const url = new URL(baseUrl);\n  if (url.protocol !== 'http:' || !['127.0.0.1', '[::1]'].includes(url.hostname)\n    || url.username || url.password || url.pathname !== '/' || url.search || url.hash)\n    throw new Error('Pet Runtime input requires a plain HTTP loopback IP origin, without credentials or a path.');\n  if (typeof threadId !== 'string' || !threadId.trim() || threadId.length > 512)\n    throw new Error('Choose one Runtime --thread ID.');\n  let sdk;\n  try { sdk = await import('@codewhale/runtime-sdk'); }\n  catch { sdk = await import('../../../npm/runtime-sdk/index.js'); }\n  if (typeof sdk.CodeWhaleRuntimeClient.prototype.threadEvents !== 'function')\n    throw new Error('The local Runtime SDK needs threadEvents support.');\n  const client = new sdk.CodeWhaleRuntimeClient({ baseUrl: url.href, token });\n  const shutdown = new AbortController();\n  const trace = new CodewhaleRuntimeTrace('Codewhale Runtime', 250_000,\n    event => privacyEvent(event, 'metadata'), 64 * 1024 * 1024);\n  let cursor = 0, revision = 0, connected = false, fatal = false;\n  const done = (async () => {\n    let backoff = 250;\n    while (!shutdown.signal.aborted && !fatal) {\n      // Fifteen-second server heartbeats make a silent, half-open connection\n      // distinguishable from an idle journal. The timeout is driver time only.\n      const attempt = new AbortController();\n      const signal = AbortSignal.any([shutdown.signal, attempt.signal]);\n      let idleTimer;\n      const refresh = () => { clearTimeout(idleTimer); idleTimer = setTimeout(() => attempt.abort(), 45_000); };\n      refresh();\n      const fetchImpl = client.fetchImpl;\n      client.fetchImpl = async (input, init) => {\n        const response = await fetchImpl(input, init);","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/scripts/lib/pet-runtime.mjs#L1-L37","documentation":"followRuntime imports the Codewhale Runtime SDK (preferentially @codewhale/runtime-sdk, falling back to the local npm/runtime-sdk bundle) and requires CodeWhaleRuntimeClient.prototype.threadEvents to be an async iterator method for live/replay journal streaming. If the resolved SDK module is old or stubbed and lacks threadEvents on the client prototype, the script refuses to proceed rather than fail mid-stream.","triggerScenarios":"Calling followRuntime (used by scripts/pet.mjs --runtime=... --thread=...) when the imported SDK module's CodeWhaleRuntimeClient class has no threadEvents function on its prototype — e.g. an outdated installed @codewhale/runtime-sdk package, or the local ../../../npm/runtime-sdk/index.js fallback exporting an older client without that API.","commonSituations":"A stale npm-installed @codewhale/runtime-sdk shadowing the repo's local copy; a workspace where npm/runtime-sdk/index.js was vendored from an older release; a mock or alias of the SDK injected via module resolution that only implements connection methods but not threadEvents.","solutions":["Update the @codewhale/runtime-sdk dependency to a version whose CodeWhaleRuntimeClient implements threadEvents (check its prototype in node REPL: typeof sdk.CodeWhaleRuntimeClient.prototype.threadEvents).","If the local fallback npm/runtime-sdk/index.js is being loaded, sync it to the current SDK build that includes threadEvents.","Check that no stub/mock/alias (e.g. import maps, symlinked node_modules) is replacing the real SDK module.","Pin the dependency in package.json/lockfile so the local and installed versions agree."],"exampleFix":"// before (old vendored SDK)\nnpm/runtime-sdk/index.js  // client without threadEvents\n// after\nnpm update @codewhale/runtime-sdk  # or re-vendor npm/runtime-sdk/index.js from a build exposing CodeWhaleRuntimeClient.prototype.threadEvents","handlingStrategy":"validation","validationCode":"const sdk = await import('@codewhale/runtime-sdk').catch(() => import('../../../npm/runtime-sdk/index.js'));\nif (typeof sdk.CodeWhaleRuntimeClient?.prototype?.threadEvents !== 'function')\n  throw new Error('Runtime SDK lacks threadEvents; upgrade @codewhale/runtime-sdk.');","typeGuard":"function supportsThreadEvents(sdk) {\n  return typeof sdk?.CodeWhaleRuntimeClient?.prototype?.threadEvents === 'function';\n}","tryCatchPattern":null,"preventionTips":["Pin and regularly update @codewhale/runtime-sdk in package.json/lockfile.","Check typeof sdk.CodeWhaleRuntimeClient.prototype.threadEvents === 'function' in a smoke test.","Keep the local npm/runtime-sdk fallback in sync with the published SDK version."],"tags":["dependency-version","runtime-sdk","api-compatibility","missing-method"],"backgroundTag":"missing-dependency","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}