@skillcap/gdh 0.13.3 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/INSTALL-BUNDLE.json +1 -1
  2. package/README.md +4 -4
  3. package/RELEASE-SPAN-UPDATE-CONTRACTS.json +158 -0
  4. package/node_modules/@gdh/adapters/package.json +8 -8
  5. package/node_modules/@gdh/authoring/package.json +2 -2
  6. package/node_modules/@gdh/cli/package.json +10 -10
  7. package/node_modules/@gdh/core/dist/index.d.ts +37 -2
  8. package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
  9. package/node_modules/@gdh/core/dist/index.js +2 -2
  10. package/node_modules/@gdh/core/dist/index.js.map +1 -1
  11. package/node_modules/@gdh/core/package.json +1 -1
  12. package/node_modules/@gdh/docs/dist/guidance.d.ts.map +1 -1
  13. package/node_modules/@gdh/docs/dist/guidance.js +1 -0
  14. package/node_modules/@gdh/docs/dist/guidance.js.map +1 -1
  15. package/node_modules/@gdh/docs/package.json +2 -2
  16. package/node_modules/@gdh/mcp/package.json +8 -8
  17. package/node_modules/@gdh/observability/dist/runtime-bundles.d.ts.map +1 -1
  18. package/node_modules/@gdh/observability/dist/runtime-bundles.js +28 -2
  19. package/node_modules/@gdh/observability/dist/runtime-bundles.js.map +1 -1
  20. package/node_modules/@gdh/observability/package.json +2 -2
  21. package/node_modules/@gdh/runtime/dist/bridge-surface.js +232 -6
  22. package/node_modules/@gdh/runtime/dist/bridge-surface.js.map +1 -1
  23. package/node_modules/@gdh/runtime/dist/index.d.ts.map +1 -1
  24. package/node_modules/@gdh/runtime/dist/index.js +427 -17
  25. package/node_modules/@gdh/runtime/dist/index.js.map +1 -1
  26. package/node_modules/@gdh/runtime/package.json +2 -2
  27. package/node_modules/@gdh/scan/package.json +3 -3
  28. package/node_modules/@gdh/verify/dist/scenarios.d.ts +3 -1
  29. package/node_modules/@gdh/verify/dist/scenarios.d.ts.map +1 -1
  30. package/node_modules/@gdh/verify/dist/scenarios.js +425 -36
  31. package/node_modules/@gdh/verify/dist/scenarios.js.map +1 -1
  32. package/node_modules/@gdh/verify/package.json +7 -7
  33. package/package.json +12 -12
@@ -1552,6 +1552,24 @@ const HOST_RUNTIME_BRIDGE_ENTRIES = [
1552
1552
  executionRoute: null,
1553
1553
  safetyLevel: "read_only",
1554
1554
  },
1555
+ {
1556
+ id: "state.node_presence.await",
1557
+ summary: "Wait for one exact node path to become present or absent through bounded polling.",
1558
+ kind: "waiter",
1559
+ shape: "composite",
1560
+ source: "core",
1561
+ executionRoute: null,
1562
+ safetyLevel: "read_only",
1563
+ },
1564
+ {
1565
+ id: "state.signal.await",
1566
+ summary: "Wait for one observed signal emission to occur after an optional observed-count baseline.",
1567
+ kind: "waiter",
1568
+ shape: "composite",
1569
+ source: "core",
1570
+ executionRoute: null,
1571
+ safetyLevel: "read_only",
1572
+ },
1555
1573
  ];
1556
1574
  export function createRuntimeBridgeManager() {
1557
1575
  const sessions = new Map();
@@ -1816,6 +1834,7 @@ export function createRuntimeBridgeManager() {
1816
1834
  summary: `Runtime bridge session "${sessionId}" was not found.`,
1817
1835
  reasons: ["bridge_session_not_found"],
1818
1836
  result: null,
1837
+ waiterEvidence: null,
1819
1838
  transcriptPath: null,
1820
1839
  };
1821
1840
  }
@@ -1827,6 +1846,7 @@ export function createRuntimeBridgeManager() {
1827
1846
  summary: `Runtime bridge session "${sessionId}" is no longer active.`,
1828
1847
  reasons: ["bridge_session_not_active"],
1829
1848
  result: null,
1849
+ waiterEvidence: null,
1830
1850
  transcriptPath: session.transcriptPath,
1831
1851
  };
1832
1852
  }
@@ -1838,6 +1858,7 @@ export function createRuntimeBridgeManager() {
1838
1858
  summary: `Runtime bridge session "${sessionId}" is already handling another invocation.`,
1839
1859
  reasons: ["bridge_session_busy"],
1840
1860
  result: null,
1861
+ waiterEvidence: null,
1841
1862
  transcriptPath: session.transcriptPath,
1842
1863
  };
1843
1864
  }
@@ -1864,6 +1885,7 @@ export function createRuntimeBridgeManager() {
1864
1885
  : `Bridge entry "${entryId}" returned state "${syntheticResponse.state}".`,
1865
1886
  reasons: syntheticResponse.error ? [syntheticResponse.error] : [],
1866
1887
  result: syntheticResponse.result,
1888
+ waiterEvidence: syntheticResponse.waiterEvidence,
1867
1889
  transcriptPath: session.transcriptPath,
1868
1890
  };
1869
1891
  }
@@ -1883,6 +1905,7 @@ export function createRuntimeBridgeManager() {
1883
1905
  : `Bridge entry "${entryId}" returned state "${response.state}".`,
1884
1906
  reasons: response.error ? [response.error] : [],
1885
1907
  result: response.result,
1908
+ waiterEvidence: null,
1886
1909
  transcriptPath: session.transcriptPath,
1887
1910
  };
1888
1911
  }
@@ -1898,6 +1921,7 @@ export function createRuntimeBridgeManager() {
1898
1921
  summary: `Bridge entry "${entryId}" failed: ${formatBridgeError(error)}.`,
1899
1922
  reasons: ["bridge_invoke_failed"],
1900
1923
  result: null,
1924
+ waiterEvidence: null,
1901
1925
  transcriptPath: session.transcriptPath,
1902
1926
  };
1903
1927
  }
@@ -1998,11 +2022,53 @@ function mergeRuntimeBridgeEntries(entries) {
1998
2022
  return merged;
1999
2023
  }
2000
2024
  async function invokeSyntheticBridgeEntry(session, entryId, inputValue) {
2025
+ if (entryId === "screenshot.capture") {
2026
+ return invokeScreenshotCaptureEntry(session, inputValue);
2027
+ }
2001
2028
  if (entryId === "state.node_property.await") {
2002
2029
  return invokeNodePropertyAwaitEntry(session, inputValue);
2003
2030
  }
2031
+ if (entryId === "state.node_presence.await") {
2032
+ return invokeNodePresenceAwaitEntry(session, inputValue);
2033
+ }
2034
+ if (entryId === "state.signal.await") {
2035
+ return invokeSignalAwaitEntry(session, inputValue);
2036
+ }
2004
2037
  return null;
2005
2038
  }
2039
+ async function invokeScreenshotCaptureEntry(session, inputValue) {
2040
+ const input = toJsonRecord(inputValue);
2041
+ const label = typeof input["label"] === "string" ? input["label"] : "screenshot";
2042
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
2043
+ const basename = `${stamp}-${sanitizeScreenshotLabel(label)}`;
2044
+ const screenshotDirectory = path.join(session.artifactDirectory, "screenshots");
2045
+ const imagePath = path.join(screenshotDirectory, `${basename}.png`);
2046
+ const metadataPath = path.join(screenshotDirectory, `${basename}.json`);
2047
+ await fs.mkdir(path.dirname(imagePath), { recursive: true });
2048
+ await fs.mkdir(path.dirname(metadataPath), { recursive: true });
2049
+ recordBridgeEvent(session, "screenshot_capture_requested", {
2050
+ imagePath,
2051
+ metadataPath,
2052
+ });
2053
+ const response = await invokeBridgeEntryOverSocket(session.ws, session.sessionId, "screenshot.capture", {
2054
+ ...input,
2055
+ imagePath,
2056
+ metadataPath,
2057
+ });
2058
+ recordBridgeEvent(session, "screenshot_capture_response", {
2059
+ state: response.state,
2060
+ result: response.result,
2061
+ error: response.error,
2062
+ imagePath,
2063
+ metadataPath,
2064
+ });
2065
+ return {
2066
+ state: response.state,
2067
+ result: response.result,
2068
+ error: response.error,
2069
+ waiterEvidence: null,
2070
+ };
2071
+ }
2006
2072
  async function invokeNodePropertyAwaitEntry(session, inputValue) {
2007
2073
  const input = toJsonRecord(inputValue);
2008
2074
  const nodePath = typeof input["nodePath"] === "string" ? input["nodePath"] : "";
@@ -2016,6 +2082,7 @@ async function invokeNodePropertyAwaitEntry(session, inputValue) {
2016
2082
  state: "failed",
2017
2083
  result: null,
2018
2084
  error: "state.node_property.await requires nodePath and property.",
2085
+ waiterEvidence: null,
2019
2086
  };
2020
2087
  }
2021
2088
  if (!hasExpected) {
@@ -2023,12 +2090,20 @@ async function invokeNodePropertyAwaitEntry(session, inputValue) {
2023
2090
  state: "failed",
2024
2091
  result: null,
2025
2092
  error: "state.node_property.await requires an expected value.",
2093
+ waiterEvidence: null,
2026
2094
  };
2027
2095
  }
2028
- const startedAt = Date.now();
2096
+ const target = {
2097
+ nodePath,
2098
+ property,
2099
+ expected,
2100
+ };
2101
+ const startedAt = new Date().toISOString();
2102
+ const startedAtMs = Date.now();
2103
+ recordWaiterStarted(session, "state.node_property.await", target, timeoutMs, pollIntervalMs);
2029
2104
  let attempts = 0;
2030
2105
  let lastValue = null;
2031
- while (Date.now() - startedAt <= timeoutMs) {
2106
+ while (Date.now() - startedAtMs <= timeoutMs) {
2032
2107
  attempts += 1;
2033
2108
  recordBridgeEvent(session, "invoke_expanded_leaf_step", {
2034
2109
  parentEntryId: "state.node_property.await",
@@ -2056,37 +2131,326 @@ async function invokeNodePropertyAwaitEntry(session, inputValue) {
2056
2131
  state: response.state,
2057
2132
  result: response.result,
2058
2133
  error: response.error,
2134
+ waiterEvidence: null,
2059
2135
  };
2060
2136
  }
2061
2137
  const payload = toJsonRecord(response.result);
2062
2138
  lastValue = (payload["value"] ?? null);
2063
2139
  if (jsonValuesEqual(lastValue, expected)) {
2140
+ const result = {
2141
+ nodePath,
2142
+ property,
2143
+ expected,
2144
+ value: lastValue,
2145
+ attempts,
2146
+ elapsedMs: Date.now() - startedAtMs,
2147
+ };
2148
+ const waiterEvidence = buildWaiterEvidence({
2149
+ waiterId: "state.node_property.await",
2150
+ outcome: "satisfied",
2151
+ startedAt,
2152
+ timeoutMs,
2153
+ pollCount: attempts,
2154
+ target,
2155
+ result,
2156
+ });
2157
+ recordWaiterSatisfied(session, "state.node_property.await", target, attempts, result);
2064
2158
  return {
2065
2159
  state: "ok",
2066
- result: {
2067
- nodePath,
2068
- property,
2069
- expected,
2070
- value: lastValue,
2071
- attempts,
2072
- elapsedMs: Date.now() - startedAt,
2073
- },
2160
+ result,
2074
2161
  error: null,
2162
+ waiterEvidence,
2075
2163
  };
2076
2164
  }
2077
2165
  await waitMs(pollIntervalMs);
2078
2166
  }
2167
+ const result = {
2168
+ nodePath,
2169
+ property,
2170
+ expected,
2171
+ value: lastValue,
2172
+ attempts,
2173
+ elapsedMs: Date.now() - startedAtMs,
2174
+ };
2175
+ const waiterEvidence = buildWaiterEvidence({
2176
+ waiterId: "state.node_property.await",
2177
+ outcome: "timed_out",
2178
+ startedAt,
2179
+ timeoutMs,
2180
+ pollCount: attempts,
2181
+ target,
2182
+ result,
2183
+ });
2184
+ recordWaiterTimedOut(session, "state.node_property.await", target, attempts, result);
2079
2185
  return {
2080
2186
  state: "unavailable",
2081
- result: {
2187
+ result,
2188
+ error: "Timed out waiting for the requested node property value.",
2189
+ waiterEvidence,
2190
+ };
2191
+ }
2192
+ async function invokeNodePresenceAwaitEntry(session, inputValue) {
2193
+ const input = toJsonRecord(inputValue);
2194
+ const nodePath = typeof input["nodePath"] === "string" ? input["nodePath"] : "";
2195
+ const expected = typeof input["expected"] === "string" ? input["expected"] : "";
2196
+ const timeoutMs = clampNumber(input["timeoutMs"], 1500, 100, 5000);
2197
+ const pollIntervalMs = clampNumber(input["pollIntervalMs"], 50, 25, 250);
2198
+ if (nodePath.length === 0) {
2199
+ return {
2200
+ state: "failed",
2201
+ result: null,
2202
+ error: "state.node_presence.await requires nodePath.",
2203
+ waiterEvidence: null,
2204
+ };
2205
+ }
2206
+ if (expected !== "present" && expected !== "absent") {
2207
+ return {
2208
+ state: "failed",
2209
+ result: null,
2210
+ error: 'state.node_presence.await requires expected = "present" or "absent".',
2211
+ waiterEvidence: null,
2212
+ };
2213
+ }
2214
+ const target = {
2215
+ nodePath,
2216
+ expected,
2217
+ };
2218
+ recordWaiterStarted(session, "state.node_presence.await", target, timeoutMs, pollIntervalMs);
2219
+ const startedAt = new Date().toISOString();
2220
+ const startedAtMs = Date.now();
2221
+ let attempts = 0;
2222
+ let lastSnapshot = null;
2223
+ while (Date.now() - startedAtMs <= timeoutMs) {
2224
+ attempts += 1;
2225
+ recordBridgeEvent(session, "invoke_expanded_leaf_step", {
2226
+ parentEntryId: "state.node_presence.await",
2227
+ leafEntryId: "state.node_presence.get",
2228
+ attempt: attempts,
2229
+ input: {
2230
+ nodePath,
2231
+ },
2232
+ });
2233
+ const response = await invokeBridgeEntryOverSocket(session.ws, session.sessionId, "state.node_presence.get", {
2082
2234
  nodePath,
2083
- property,
2084
- expected,
2085
- value: lastValue,
2086
- attempts,
2087
- elapsedMs: Date.now() - startedAt,
2235
+ });
2236
+ recordBridgeEvent(session, "invoke_expanded_leaf_response", {
2237
+ parentEntryId: "state.node_presence.await",
2238
+ leafEntryId: "state.node_presence.get",
2239
+ attempt: attempts,
2240
+ state: response.state,
2241
+ result: response.result,
2242
+ error: response.error,
2243
+ });
2244
+ if (response.state !== "ok") {
2245
+ return {
2246
+ state: response.state,
2247
+ result: response.result,
2248
+ error: response.error,
2249
+ waiterEvidence: null,
2250
+ };
2251
+ }
2252
+ const payload = toJsonRecord(response.result);
2253
+ lastSnapshot = payload;
2254
+ const present = payload["present"] === true;
2255
+ const matches = expected === "present" ? present : !present;
2256
+ if (matches) {
2257
+ const result = {
2258
+ nodePath,
2259
+ expected,
2260
+ present,
2261
+ name: (payload["name"] ?? null),
2262
+ className: (payload["className"] ?? null),
2263
+ attempts,
2264
+ elapsedMs: Date.now() - startedAtMs,
2265
+ };
2266
+ const waiterEvidence = buildWaiterEvidence({
2267
+ waiterId: "state.node_presence.await",
2268
+ outcome: "satisfied",
2269
+ startedAt,
2270
+ timeoutMs,
2271
+ pollCount: attempts,
2272
+ target,
2273
+ result,
2274
+ });
2275
+ recordWaiterSatisfied(session, "state.node_presence.await", target, attempts, result);
2276
+ return {
2277
+ state: "ok",
2278
+ result,
2279
+ error: null,
2280
+ waiterEvidence,
2281
+ };
2282
+ }
2283
+ await waitMs(pollIntervalMs);
2284
+ }
2285
+ const result = {
2286
+ nodePath,
2287
+ expected,
2288
+ present: lastSnapshot?.["present"] === true,
2289
+ name: (lastSnapshot?.["name"] ?? null),
2290
+ className: (lastSnapshot?.["className"] ?? null),
2291
+ attempts,
2292
+ elapsedMs: Date.now() - startedAtMs,
2293
+ };
2294
+ const waiterEvidence = buildWaiterEvidence({
2295
+ waiterId: "state.node_presence.await",
2296
+ outcome: "timed_out",
2297
+ startedAt,
2298
+ timeoutMs,
2299
+ pollCount: attempts,
2300
+ target,
2301
+ result,
2302
+ });
2303
+ recordWaiterTimedOut(session, "state.node_presence.await", target, attempts, result);
2304
+ return {
2305
+ state: "unavailable",
2306
+ result,
2307
+ error: "Timed out waiting for the requested node presence state.",
2308
+ waiterEvidence,
2309
+ };
2310
+ }
2311
+ async function invokeSignalAwaitEntry(session, inputValue) {
2312
+ const input = toJsonRecord(inputValue);
2313
+ const nodePath = typeof input["nodePath"] === "string" ? input["nodePath"] : "";
2314
+ const signalName = typeof input["signalName"] === "string" ? input["signalName"] : "";
2315
+ const afterCount = clampCount(input["afterCount"], 0);
2316
+ const timeoutMs = clampNumber(input["timeoutMs"], 1500, 100, 5000);
2317
+ const pollIntervalMs = clampNumber(input["pollIntervalMs"], 50, 25, 250);
2318
+ if (nodePath.length === 0 || signalName.length === 0) {
2319
+ return {
2320
+ state: "failed",
2321
+ result: null,
2322
+ error: "state.signal.await requires nodePath and signalName.",
2323
+ waiterEvidence: null,
2324
+ };
2325
+ }
2326
+ const target = {
2327
+ nodePath,
2328
+ signalName,
2329
+ afterCount,
2330
+ };
2331
+ recordWaiterStarted(session, "state.signal.await", target, timeoutMs, pollIntervalMs);
2332
+ recordBridgeEvent(session, "invoke_expanded_leaf_step", {
2333
+ parentEntryId: "state.signal.await",
2334
+ leafEntryId: "state.signal_observation.start",
2335
+ attempt: 1,
2336
+ input: {
2337
+ nodePath,
2338
+ signalName,
2088
2339
  },
2089
- error: "Timed out waiting for the requested node property value.",
2340
+ });
2341
+ const startResponse = await invokeBridgeEntryOverSocket(session.ws, session.sessionId, "state.signal_observation.start", {
2342
+ nodePath,
2343
+ signalName,
2344
+ });
2345
+ recordBridgeEvent(session, "invoke_expanded_leaf_response", {
2346
+ parentEntryId: "state.signal.await",
2347
+ leafEntryId: "state.signal_observation.start",
2348
+ attempt: 1,
2349
+ state: startResponse.state,
2350
+ result: startResponse.result,
2351
+ error: startResponse.error,
2352
+ });
2353
+ if (startResponse.state !== "ok") {
2354
+ return {
2355
+ state: startResponse.state,
2356
+ result: startResponse.result,
2357
+ error: startResponse.error,
2358
+ waiterEvidence: null,
2359
+ };
2360
+ }
2361
+ const startedAt = new Date().toISOString();
2362
+ const startedAtMs = Date.now();
2363
+ let attempts = 0;
2364
+ let lastSnapshot = null;
2365
+ while (Date.now() - startedAtMs <= timeoutMs) {
2366
+ attempts += 1;
2367
+ recordBridgeEvent(session, "invoke_expanded_leaf_step", {
2368
+ parentEntryId: "state.signal.await",
2369
+ leafEntryId: "state.signal_observation.get",
2370
+ attempt: attempts,
2371
+ input: {
2372
+ nodePath,
2373
+ signalName,
2374
+ },
2375
+ });
2376
+ const response = await invokeBridgeEntryOverSocket(session.ws, session.sessionId, "state.signal_observation.get", {
2377
+ nodePath,
2378
+ signalName,
2379
+ });
2380
+ recordBridgeEvent(session, "invoke_expanded_leaf_response", {
2381
+ parentEntryId: "state.signal.await",
2382
+ leafEntryId: "state.signal_observation.get",
2383
+ attempt: attempts,
2384
+ state: response.state,
2385
+ result: response.result,
2386
+ error: response.error,
2387
+ });
2388
+ if (response.state !== "ok") {
2389
+ return {
2390
+ state: response.state,
2391
+ result: response.result,
2392
+ error: response.error,
2393
+ waiterEvidence: null,
2394
+ };
2395
+ }
2396
+ const payload = toJsonRecord(response.result);
2397
+ lastSnapshot = payload;
2398
+ const count = clampCount(payload["count"], 0);
2399
+ if (count > afterCount) {
2400
+ const result = {
2401
+ nodePath,
2402
+ signalName,
2403
+ afterCount,
2404
+ count,
2405
+ lastObservedAt: (payload["lastObservedAt"] ?? null),
2406
+ lastPayload: (payload["lastPayload"] ?? null),
2407
+ attempts,
2408
+ elapsedMs: Date.now() - startedAtMs,
2409
+ };
2410
+ const waiterEvidence = buildWaiterEvidence({
2411
+ waiterId: "state.signal.await",
2412
+ outcome: "satisfied",
2413
+ startedAt,
2414
+ timeoutMs,
2415
+ pollCount: attempts,
2416
+ target,
2417
+ result,
2418
+ });
2419
+ recordWaiterSatisfied(session, "state.signal.await", target, attempts, result);
2420
+ return {
2421
+ state: "ok",
2422
+ result,
2423
+ error: null,
2424
+ waiterEvidence,
2425
+ };
2426
+ }
2427
+ await waitMs(pollIntervalMs);
2428
+ }
2429
+ const result = {
2430
+ nodePath,
2431
+ signalName,
2432
+ afterCount,
2433
+ count: clampCount(lastSnapshot?.["count"], 0),
2434
+ lastObservedAt: (lastSnapshot?.["lastObservedAt"] ?? null),
2435
+ lastPayload: (lastSnapshot?.["lastPayload"] ?? null),
2436
+ attempts,
2437
+ elapsedMs: Date.now() - startedAtMs,
2438
+ };
2439
+ const waiterEvidence = buildWaiterEvidence({
2440
+ waiterId: "state.signal.await",
2441
+ outcome: "timed_out",
2442
+ startedAt,
2443
+ timeoutMs,
2444
+ pollCount: attempts,
2445
+ target,
2446
+ result,
2447
+ });
2448
+ recordWaiterTimedOut(session, "state.signal.await", target, attempts, result);
2449
+ return {
2450
+ state: "unavailable",
2451
+ result,
2452
+ error: "Timed out waiting for the requested signal emission.",
2453
+ waiterEvidence,
2090
2454
  };
2091
2455
  }
2092
2456
  function toJsonRecord(value) {
@@ -2101,6 +2465,16 @@ function clampNumber(value, fallback, minimum, maximum) {
2101
2465
  }
2102
2466
  return Math.max(minimum, Math.min(maximum, Math.trunc(value)));
2103
2467
  }
2468
+ function clampCount(value, fallback) {
2469
+ if (typeof value !== "number" || !Number.isFinite(value)) {
2470
+ return fallback;
2471
+ }
2472
+ return Math.max(0, Math.trunc(value));
2473
+ }
2474
+ function sanitizeScreenshotLabel(value) {
2475
+ const sanitized = value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
2476
+ return (sanitized || "screenshot").slice(0, 80);
2477
+ }
2104
2478
  function jsonValuesEqual(left, right) {
2105
2479
  return JSON.stringify(left) === JSON.stringify(right);
2106
2480
  }
@@ -2125,6 +2499,42 @@ function recordBridgeEvent(session, event, payload) {
2125
2499
  ...payload,
2126
2500
  });
2127
2501
  }
2502
+ function recordWaiterStarted(session, entryId, target, timeoutMs, pollIntervalMs) {
2503
+ recordBridgeEvent(session, "invoke_waiter_started", {
2504
+ entryId,
2505
+ target,
2506
+ timeoutMs,
2507
+ pollIntervalMs,
2508
+ });
2509
+ }
2510
+ function recordWaiterSatisfied(session, entryId, target, attempts, result) {
2511
+ recordBridgeEvent(session, "invoke_waiter_satisfied", {
2512
+ entryId,
2513
+ target,
2514
+ attempts,
2515
+ result,
2516
+ });
2517
+ }
2518
+ function recordWaiterTimedOut(session, entryId, target, attempts, result) {
2519
+ recordBridgeEvent(session, "invoke_waiter_timed_out", {
2520
+ entryId,
2521
+ target,
2522
+ attempts,
2523
+ result,
2524
+ });
2525
+ }
2526
+ function buildWaiterEvidence(input) {
2527
+ return {
2528
+ waiterId: input.waiterId,
2529
+ outcome: input.outcome,
2530
+ startedAt: input.startedAt,
2531
+ finishedAt: new Date().toISOString(),
2532
+ timeoutMs: input.timeoutMs,
2533
+ pollCount: input.pollCount,
2534
+ target: input.target,
2535
+ result: input.result,
2536
+ };
2537
+ }
2128
2538
  function bridgeMissing(reason) {
2129
2539
  return {
2130
2540
  required: true,