@danceiny/gotry 0.0.1-rc.17 → 0.0.1-rc.19

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 (137) hide show
  1. package/README.md +185 -148
  2. package/README.zh-CN.md +195 -154
  3. package/bin/gotry-bootstrap.js +246 -26
  4. package/bin/gotry-inner.js +68 -38
  5. package/bin/gotry-runtime-resolution.d.ts +1 -1
  6. package/bin/gotry-runtime-resolution.js +7 -8
  7. package/cordis.gotry-patch.yml +15 -5
  8. package/data/stations-12306-verify.json +524 -0
  9. package/dist/capabilities/agent-reach.js +4 -4
  10. package/dist/capabilities/channel-health.js +103 -0
  11. package/dist/capabilities/channel-registry.js +213 -0
  12. package/dist/capabilities/doctor.js +285 -0
  13. package/dist/capabilities/effect.js +178 -5
  14. package/dist/capabilities/flyai.js +13 -0
  15. package/dist/capabilities/session/adapters/ctrip-hotel.js +207 -0
  16. package/dist/capabilities/session/adapters/rail-12306.js +285 -0
  17. package/dist/capabilities/session/extension-bridge.js +14 -1
  18. package/dist/capabilities/session/extension-channel.js +4 -2
  19. package/dist/capabilities/session/extension-distribution.js +1 -1
  20. package/dist/capabilities/session-search.js +311 -0
  21. package/dist/capabilities/visa-policy.js +126 -0
  22. package/dist/scripts/agent-planning-turn-deadline-e2e.js +304 -0
  23. package/dist/scripts/agent-planning-turn-deadline-tests.js +281 -0
  24. package/dist/scripts/benchmark-environment-bridge-e2e.js +5 -10
  25. package/dist/scripts/benchmark-environment-bridge-tests.js +148 -142
  26. package/dist/scripts/booking-copilot-availability-ledger-binding-tests.js +26 -26
  27. package/dist/scripts/{booking-copilot-availability-policy-v2-tests.js → booking-copilot-availability-policy-tests.js} +115 -115
  28. package/dist/scripts/booking-copilot-crossrepo-fixture-server.js +41 -1
  29. package/dist/scripts/booking-copilot-dsh-core-proof-tests.js +75 -120
  30. package/dist/scripts/booking-copilot-dsh-planner-proof-tests.js +370 -61
  31. package/dist/scripts/booking-copilot-dsh-plugin-proof-tests.js +2 -2
  32. package/dist/scripts/booking-copilot-event-sequence-concurrency-proof-tests.js +7 -3
  33. package/dist/scripts/booking-copilot-gap-code-contract-proof-tests.js +15 -9
  34. package/dist/scripts/booking-copilot-operation-ledger-concurrency-proof-tests.js +19 -11
  35. package/dist/scripts/booking-copilot-receipt-ledger-concurrency-proof-tests.js +16 -52
  36. package/dist/scripts/booking-copilot-runtime-proof-tests.js +3953 -155
  37. package/dist/scripts/booking-copilot-server-proof-tests.js +58 -23
  38. package/dist/scripts/booking-copilot-startup-proof-tests.js +10 -119
  39. package/dist/scripts/booking-surface-contract-proof-tests.js +1154 -326
  40. package/dist/scripts/bootstrap-tests.js +115 -44
  41. package/dist/scripts/build-metrics-report.js +339 -0
  42. package/dist/scripts/channel-probe-tests.js +148 -0
  43. package/dist/scripts/channel-probe.js +252 -0
  44. package/dist/scripts/channel-registry-tests.js +262 -0
  45. package/dist/scripts/doctor-tests.js +121 -0
  46. package/dist/scripts/effect-tests.js +274 -1
  47. package/dist/scripts/extension-tests.js +129 -11
  48. package/dist/scripts/fact-gate-tests.js +91 -2
  49. package/dist/scripts/flyai-tests.js +17 -1
  50. package/dist/scripts/hbcli-e2e-tests.js +1 -3
  51. package/dist/scripts/metrics-report-tests.js +288 -0
  52. package/dist/scripts/persona-surface-guard-tests.js +19 -0
  53. package/dist/scripts/session-tests.js +401 -1
  54. package/dist/scripts/smoke.js +100 -83
  55. package/dist/scripts/turn-handoff-collect-tests.js +216 -0
  56. package/dist/scripts/turn-handoff-collect.js +189 -0
  57. package/dist/scripts/turn-policy-tests.js +56 -0
  58. package/dist/scripts/typed-contract-canary.js +261 -0
  59. package/dist/scripts/visa-policy-tests.js +75 -0
  60. package/dist/scripts/wish-channel-gate-tests.js +111 -0
  61. package/dist/src/artifact-gate.js +91 -3
  62. package/dist/src/benchmark-agent-conformance.js +7 -4
  63. package/dist/src/benchmark-environment-bridge.js +68 -7
  64. package/dist/src/bookable-facts.js +62 -3
  65. package/dist/src/booking-surface/{availability-policy-v2.js → availability-policy.js} +89 -89
  66. package/dist/src/booking-surface/canonical-schema.js +8 -22
  67. package/dist/src/booking-surface/contracts.js +70 -19
  68. package/dist/src/booking-surface/dsh-planner.js +310 -177
  69. package/dist/src/booking-surface/dsh-plugin.js +3 -3
  70. package/dist/src/booking-surface/index.js +1 -5
  71. package/dist/src/booking-surface/profile.js +1 -1
  72. package/dist/src/booking-surface/runtime.js +1682 -247
  73. package/dist/src/booking-surface/server.js +371 -185
  74. package/dist/src/booking-surface/startup.js +17 -27
  75. package/dist/src/booking-surface/validation.js +270 -760
  76. package/dist/src/index.js +735 -149
  77. package/dist/src/turn-deadline.js +292 -0
  78. package/dist/src/turn-policy.js +146 -0
  79. package/dist/src/wish-pool.js +5 -0
  80. package/extension/background.js +23 -4
  81. package/extension/content-main.js +47 -9
  82. package/extension/manifest.json +27 -9
  83. package/package.json +6 -26
  84. package/schemas/booking.surface.schema.json +2593 -0
  85. package/ts/capabilities/agent-reach.ts +4 -4
  86. package/ts/capabilities/flyai.ts +21 -3
  87. package/ts/capabilities/session/action-cache.ts +1 -1
  88. package/ts/capabilities/session/adapters/ctrip-hotel.ts +238 -0
  89. package/ts/capabilities/session/adapters/rail-12306.ts +293 -0
  90. package/ts/capabilities/session/extension-bridge.ts +32 -2
  91. package/ts/capabilities/session/extension-channel.ts +9 -8
  92. package/ts/capabilities/session/extension-distribution.ts +3 -1
  93. package/ts/capabilities/session/health-watch.ts +1 -1
  94. package/ts/capabilities/session/read-guard.ts +1 -1
  95. package/ts/capabilities/session/wizard.ts +1 -1
  96. package/ts/capabilities/session-search.ts +323 -1
  97. package/ts/package.json +1 -0
  98. package/ts/scripts/turn-handoff-collect.ts +178 -0
  99. package/ts/src/artifact-gate.ts +82 -2
  100. package/ts/src/benchmark-agent-conformance.ts +14 -6
  101. package/ts/src/benchmark-environment-bridge.ts +29 -10
  102. package/ts/src/bookable-facts.ts +109 -5
  103. package/ts/src/booking-saga.ts +1 -1
  104. package/ts/src/booking-surface/{availability-policy-v2.ts → availability-policy.ts} +124 -125
  105. package/ts/src/booking-surface/canonical-schema.js +8 -22
  106. package/ts/src/booking-surface/contracts.ts +278 -239
  107. package/ts/src/booking-surface/dsh-planner.ts +333 -194
  108. package/ts/src/booking-surface/dsh-plugin.js +3 -3
  109. package/ts/src/booking-surface/index.ts +1 -5
  110. package/ts/src/booking-surface/profile.ts +11 -11
  111. package/ts/src/booking-surface/runtime.ts +1351 -351
  112. package/ts/src/booking-surface/server.ts +282 -175
  113. package/ts/src/booking-surface/startup.ts +36 -48
  114. package/ts/src/booking-surface/validation.ts +194 -442
  115. package/ts/src/contracts.ts +1 -1
  116. package/ts/src/index.ts +489 -171
  117. package/ts/src/loop.ts +1 -1
  118. package/ts/src/state-ledger.ts +1 -1
  119. package/ts/src/turn-deadline.ts +361 -0
  120. package/ts/src/turn-policy.ts +154 -0
  121. package/ts/src/wish-pool.ts +17 -4
  122. package/dist/scripts/agent-planning-budget-e2e.js +0 -227
  123. package/dist/scripts/agent-planning-budget-tests.js +0 -173
  124. package/dist/scripts/booking-copilot-v2-runtime-proof-tests.js +0 -3935
  125. package/dist/scripts/booking-surface-v2-contract-proof-tests.js +0 -1174
  126. package/dist/src/booking-surface/contracts-v2.js +0 -89
  127. package/dist/src/booking-surface/runtime-v2.js +0 -1771
  128. package/dist/src/booking-surface/server-v2.js +0 -334
  129. package/dist/src/booking-surface/validation-v2.js +0 -319
  130. package/dist/src/tool-budget.js +0 -136
  131. package/schemas/booking.surface.v1.schema.json +0 -927
  132. package/schemas/booking.surface.v2.schema.json +0 -61
  133. package/ts/src/booking-surface/contracts-v2.ts +0 -118
  134. package/ts/src/booking-surface/runtime-v2.ts +0 -1466
  135. package/ts/src/booking-surface/server-v2.ts +0 -247
  136. package/ts/src/booking-surface/validation-v2.ts +0 -205
  137. package/ts/src/tool-budget.ts +0 -165
@@ -1,7 +1,7 @@
1
1
  import assert from 'node:assert/strict';
2
- import { validateActionReceiptV1 } from '../src/booking-surface/validation.js';
2
+ import { validateBookingSurface } from '../src/booking-surface/validation.js';
3
3
  const receiptBase = {
4
- schemaVersion: 'booking.surface.v1',
4
+ schemaVersion: 'booking.surface',
5
5
  kind: 'action.receipt',
6
6
  actionId: 'offers-query-gap-contract',
7
7
  contextRef: 'ctx-gap-contract',
@@ -36,7 +36,9 @@ const oldRatesFailedReceipt = {
36
36
  ...ratesFailedResult,
37
37
  gapCodes: [
38
38
  'hotel_rates_failed:hotel-1'
39
- ]
39
+ ],
40
+ blockers: [],
41
+ relaxationsApplied: []
40
42
  }
41
43
  };
42
44
  const newRatesFailedReceipt = {
@@ -45,11 +47,13 @@ const newRatesFailedReceipt = {
45
47
  ...ratesFailedResult,
46
48
  gapCodes: [
47
49
  'hotel_rates_failed'
48
- ]
50
+ ],
51
+ blockers: [],
52
+ relaxationsApplied: []
49
53
  }
50
54
  };
51
- assert.equal(validateActionReceiptV1(oldRatesFailedReceipt).ok, false, 'legacy dynamic supplier gap code must fail validation');
52
- assert.deepEqual(validateActionReceiptV1(newRatesFailedReceipt), {
55
+ assert.equal(validateBookingSurface(oldRatesFailedReceipt).ok, false, 'legacy dynamic supplier gap code must fail validation');
56
+ assert.deepEqual(validateBookingSurface(newRatesFailedReceipt), {
53
57
  ok: true
54
58
  }, 'fixed supplier gap code with hotel fact ref must validate');
55
59
  assert.deepEqual(newRatesFailedReceipt.resultContract.factRefs, [
@@ -77,7 +81,9 @@ const oldNotVisibleReceipt = {
77
81
  ],
78
82
  gapCodes: [
79
83
  'hotel_not_visible:not-visible'
80
- ]
84
+ ],
85
+ blockers: [],
86
+ relaxationsApplied: []
81
87
  }
82
88
  };
83
89
  const newNotVisibleReceipt = {
@@ -89,8 +95,8 @@ const newNotVisibleReceipt = {
89
95
  ]
90
96
  }
91
97
  };
92
- assert.equal(validateActionReceiptV1(oldNotVisibleReceipt).ok, false, 'legacy dynamic visibility gap code must fail validation');
93
- assert.deepEqual(validateActionReceiptV1(newNotVisibleReceipt), {
98
+ assert.equal(validateBookingSurface(oldNotVisibleReceipt).ok, false, 'legacy dynamic visibility gap code must fail validation');
99
+ assert.deepEqual(validateBookingSurface(newNotVisibleReceipt), {
94
100
  ok: true
95
101
  }, 'fixed visibility gap code with hotel fact ref must validate');
96
102
  assert.deepEqual(newNotVisibleReceipt.resultContract.factRefs, [
@@ -8,7 +8,7 @@ import { ensureLedger } from '../src/state-ledger.js';
8
8
  import { BookingCopilotTaskRuntime } from '../src/booking-surface/runtime.js';
9
9
  const taskId = 'task-operation-atomicity';
10
10
  const workspace = {
11
- schemaVersion: 'booking.surface.v1',
11
+ schemaVersion: 'booking.surface',
12
12
  contextRef: 'ctx-operation-atomicity',
13
13
  surface: 'tenant',
14
14
  revision: 0,
@@ -30,19 +30,19 @@ const workspace = {
30
30
  }
31
31
  };
32
32
  const base = {
33
- schemaVersion: 'booking.surface.v1',
33
+ schemaVersion: 'booking.surface',
34
34
  kind: 'search.patch',
35
35
  actionId: 'action-operation-atomicity',
36
36
  contextRef: workspace.contextRef,
37
37
  expectedRevision: 0,
38
- reason: 'raw-reason-marker@example.invalid Bearer RAW_SECRET',
38
+ reason: 'raw-reason-marker',
39
39
  factRefs: [
40
40
  'fact:opaque'
41
41
  ],
42
42
  input: {
43
43
  patch: {
44
44
  destination: {
45
- query: 'raw-input-marker@example.invalid'
45
+ query: 'typed-input-marker'
46
46
  }
47
47
  }
48
48
  }
@@ -207,9 +207,10 @@ async function run(kind, conflictVariant = 'conflict') {
207
207
  const ledger = ensureLedger(root);
208
208
  const runtime = new BookingCopilotTaskRuntime(ledger);
209
209
  const turn = {
210
- schemaVersion: 'booking.surface.v1',
210
+ schemaVersion: 'booking.surface',
211
211
  kind: 'user.turn',
212
212
  taskId,
213
+ turnId: 'operation-anchor-turn',
213
214
  workspace,
214
215
  request: {
215
216
  text: 'opaque'
@@ -224,7 +225,8 @@ async function run(kind, conflictVariant = 'conflict') {
224
225
  if (kind === 'same') assert.deepEqual(success[0].event, success[1].event);
225
226
  assert.equal(ledger.db.prepare("SELECT COUNT(*) AS n FROM events WHERE kind = 'booking.copilot.action.issued'").get().n, 1);
226
227
  const durable = ledger.db.prepare("SELECT payload FROM events WHERE kind = 'booking.copilot.action.issued'").get();
227
- assert.ok(!/raw-reason-marker|raw-input-marker|Bearer RAW_SECRET|@example|secret/i.test(durable.payload));
228
+ assert.ok(!/raw-reason-marker|@example|secret/i.test(durable.payload), 'action reason prose is never durable');
229
+ assert.match(durable.payload, /"reasonDigest"/, 'the redacted reason digest is durable');
228
230
  if (kind === 'same') {
229
231
  const event = success[0].event;
230
232
  ledger.close();
@@ -308,28 +310,33 @@ if (!child) {
308
310
  const unsafeLedger = ensureLedger(unsafeRoot);
309
311
  const unsafeRuntime = new BookingCopilotTaskRuntime(unsafeLedger);
310
312
  unsafeRuntime.startTask({
311
- schemaVersion: 'booking.surface.v1',
313
+ schemaVersion: 'booking.surface',
312
314
  kind: 'user.turn',
313
315
  taskId,
316
+ turnId: 'unsafe-anchor-turn',
314
317
  workspace,
315
318
  request: {
316
319
  text: 'opaque'
317
320
  }
318
321
  });
322
+ assert.throws(()=>unsafeRuntime.issueOperation(taskId, {
323
+ ...base,
324
+ reason: 'raw prose marker@example.invalid'
325
+ }), /invalid_action|unsafe/, 'email prose is rejected before an operation is issued');
319
326
  assert.throws(()=>unsafeRuntime.issueOperation(taskId, {
320
327
  ...base,
321
328
  factRefs: [
322
329
  'attacker@example.invalid'
323
330
  ]
324
- }), /unsafe_fact_ref/);
331
+ }), /invalid_action|unsafe_fact_ref/);
325
332
  assert.throws(()=>unsafeRuntime.issueOperation(taskId, {
326
333
  ...base,
327
334
  actionId: 'attacker@example.invalid'
328
- }), /unsafe_action_id/);
335
+ }), /invalid_action|unsafe_action_id/);
329
336
  assert.throws(()=>unsafeRuntime.issueOperation(taskId, {
330
337
  ...base,
331
338
  actionId: 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.signature'
332
- }), /unsafe_action_id/);
339
+ }), /invalid_action|unsafe_action_id/);
333
340
  assert.equal(unsafeLedger.countEvents(), 2);
334
341
  unsafeLedger.close();
335
342
  rmSync(unsafeRoot, {
@@ -345,9 +352,10 @@ if (!child) {
345
352
  const tamperLedger = ensureLedger(tamperRoot);
346
353
  const tamperRuntime = new BookingCopilotTaskRuntime(tamperLedger);
347
354
  tamperRuntime.startTask({
348
- schemaVersion: 'booking.surface.v1',
355
+ schemaVersion: 'booking.surface',
349
356
  kind: 'user.turn',
350
357
  taskId,
358
+ turnId: 'tamper-anchor-turn',
351
359
  workspace,
352
360
  request: {
353
361
  text: 'opaque'
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { ensureLedger } from '../src/state-ledger.js';
8
8
  import { BookingCopilotTaskRuntime } from '../src/booking-surface/runtime.js';
9
- import { validateActionReceiptV1 } from '../src/booking-surface/validation.js';
9
+ import { validateBookingSurface } from '../src/booking-surface/validation.js';
10
10
  const childIndex = process.argv.indexOf('--child');
11
11
  const child = childIndex >= 0;
12
12
  const root = child ? process.argv[childIndex + 1] : undefined;
@@ -14,7 +14,7 @@ const actionId = child ? process.argv[childIndex + 2] : undefined;
14
14
  const variant = child ? process.argv[childIndex + 3] : undefined;
15
15
  const release = root ? join(root, 'release') : '';
16
16
  const workspace = {
17
- schemaVersion: 'booking.surface.v1',
17
+ schemaVersion: 'booking.surface',
18
18
  contextRef: 'ctx-concurrency-opaque',
19
19
  surface: 'tenant',
20
20
  revision: 0,
@@ -35,7 +35,7 @@ const workspace = {
35
35
  }
36
36
  };
37
37
  const action = {
38
- schemaVersion: 'booking.surface.v1',
38
+ schemaVersion: 'booking.surface',
39
39
  kind: 'search.run',
40
40
  actionId: 'action-concurrency-opaque',
41
41
  contextRef: workspace.contextRef,
@@ -48,7 +48,7 @@ const action = {
48
48
  };
49
49
  function receipt(which) {
50
50
  const value = {
51
- schemaVersion: 'booking.surface.v1',
51
+ schemaVersion: 'booking.surface',
52
52
  kind: 'action.receipt',
53
53
  actionId: action.actionId,
54
54
  contextRef: workspace.contextRef,
@@ -65,7 +65,9 @@ function receipt(which) {
65
65
  factRefs: [
66
66
  `fact-${which === 'same-a' || which === 'same-b' ? 'same' : which}`
67
67
  ],
68
- gapCodes: []
68
+ gapCodes: [],
69
+ blockers: [],
70
+ relaxationsApplied: []
69
71
  },
70
72
  undoToken: `undo-${which === 'same-a' || which === 'same-b' ? 'same' : which}`
71
73
  };
@@ -84,7 +86,7 @@ function receipt(which) {
84
86
  }
85
87
  function continuation(r, taskId = 'task-concurrency-opaque') {
86
88
  return {
87
- schemaVersion: 'booking.surface.v1',
89
+ schemaVersion: 'booking.surface',
88
90
  kind: 'action.receipt.continuation',
89
91
  taskId,
90
92
  workspace: {
@@ -225,17 +227,18 @@ if (!child) {
225
227
  const ledger = ensureLedger(stateRoot);
226
228
  const r = new BookingCopilotTaskRuntime(ledger);
227
229
  const turn = {
228
- schemaVersion: 'booking.surface.v1',
230
+ schemaVersion: 'booking.surface',
229
231
  kind: 'user.turn',
230
232
  taskId: 'task-concurrency-opaque',
233
+ turnId: 'receipt-anchor-turn',
231
234
  workspace,
232
235
  request: {
233
- text: 'unique-pii-marker@example.invalid Bearer TEST raw-prompt-marker'
236
+ text: 'unique-pii-marker raw-prompt-marker'
234
237
  }
235
238
  };
236
239
  const sensitiveAction = {
237
240
  ...action,
238
- reason: 'unique-pii-marker@example.invalid Bearer TEST raw-prompt-marker',
241
+ reason: 'unique-pii-marker raw-prompt-marker',
239
242
  input: {}
240
243
  };
241
244
  r.startTask(turn);
@@ -254,12 +257,12 @@ if (!child) {
254
257
  ]
255
258
  }
256
259
  })), /invalid_receipt_continuation/);
257
- assert.equal(validateActionReceiptV1({
260
+ assert.equal(validateBookingSurface({
258
261
  ...receipt('a'),
259
262
  status: 'no_match',
260
263
  observation: {
261
264
  kind: 'gap',
262
- code: 'no_match',
265
+ code: 'no_hotels_matched',
263
266
  factRefs: [
264
267
  '123'
265
268
  ]
@@ -271,7 +274,7 @@ if (!child) {
271
274
  const after = ledger;
272
275
  assert.equal(after.db.prepare("SELECT COUNT(*) AS n FROM events WHERE kind = 'booking.copilot.receipt.observed'").get().n, 1);
273
276
  const persisted = after.db.prepare("SELECT payload FROM events WHERE kind LIKE 'booking.copilot.%'").all();
274
- assert.ok(!persisted.some((row)=>/unique-pii-marker|Bearer TEST|raw-prompt-marker/i.test(row.payload)));
277
+ assert.ok(!persisted.some((row)=>/unique-pii-marker|raw-prompt-marker/i.test(row.payload)));
275
278
  const winner = differing.find((x)=>x.result === 'success');
276
279
  assert.ok(winner?.variant);
277
280
  assert.deepEqual(new BookingCopilotTaskRuntime(after).resumeTask(turn.taskId)?.lastReceipt, receipt(winner.variant));
@@ -393,7 +396,7 @@ if (!child) {
393
396
  }));
394
397
  const reuseEvents = reuseLedger.countEvents();
395
398
  const reuseState = reuseRuntime.resumeTask('task-concurrency-opaque');
396
- assert.throws(()=>reuseRuntime.issueOperation('task-concurrency-opaque', action), /action_already_receipted/);
399
+ assert.throws(()=>reuseRuntime.issueOperation('task-concurrency-opaque', action), /stale_revision|ledger_write_failed:action|action_already_receipted/);
397
400
  assert.equal(reuseLedger.countEvents(), reuseEvents);
398
401
  assert.deepEqual(reuseRuntime.resumeTask('task-concurrency-opaque'), reuseState);
399
402
  reuseLedger.close();
@@ -401,45 +404,6 @@ if (!child) {
401
404
  recursive: true,
402
405
  force: true
403
406
  });
404
- const tupleRoot = mkdtempSync(join(tmpdir(), 'gotry-receipt-tuple-'));
405
- const tupleLedger = ensureLedger(tupleRoot);
406
- const tupleRuntime = new BookingCopilotTaskRuntime(tupleLedger);
407
- for (const [taskId, actionId] of [
408
- [
409
- 'task:a',
410
- 'b'
411
- ],
412
- [
413
- 'task',
414
- 'a:b'
415
- ]
416
- ]){
417
- tupleRuntime.startTask({
418
- ...turn,
419
- taskId
420
- });
421
- tupleRuntime.issueOperation(taskId, {
422
- ...action,
423
- actionId
424
- });
425
- tupleRuntime.continueWithReceipt(continuation({
426
- ...receipt('same'),
427
- actionId
428
- }, taskId));
429
- }
430
- assert.equal(tupleLedger.db.prepare("SELECT COUNT(*) AS n FROM events WHERE kind = 'booking.copilot.action.issued'").get().n, 2);
431
- assert.equal(tupleLedger.db.prepare("SELECT COUNT(DISTINCT idem_key) AS n FROM events WHERE kind = 'booking.copilot.action.issued'").get().n, 2);
432
- assert.equal(tupleLedger.db.prepare("SELECT COUNT(*) AS n FROM events WHERE kind = 'booking.copilot.receipt.observed'").get().n, 2);
433
- assert.equal(tupleLedger.db.prepare("SELECT COUNT(DISTINCT idem_key) AS n FROM events WHERE kind = 'booking.copilot.receipt.observed'").get().n, 2);
434
- for (const taskId of [
435
- 'task:a',
436
- 'task'
437
- ])assert.equal(tupleRuntime.resumeTask(taskId)?.phase, 'planning');
438
- tupleLedger.close();
439
- rmSync(tupleRoot, {
440
- recursive: true,
441
- force: true
442
- });
443
407
  console.log('BOOKING COPILOT RECEIPT LEDGER CONCURRENCY PROOF: winner/conflict/atomic persistence OK');
444
408
  }
445
409