@hew-lang/sandbox-vm 0.6.0-rc7 → 0.6.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.
@@ -5,6 +5,7 @@
5
5
  /// where the package says `copy_value`, releases where it says `destroy_value`,
6
6
  /// follows the `unwind` edge the package names, and takes the failure edge a
7
7
  /// `checked.binary` names rather than trapping on its own.
8
+ import { executeCodec } from "./codecs.js";
8
9
  import { TraceBuilder, runtimeFailure } from "../trace.js";
9
10
  import { UNIT, cloneValue, toJsonValue } from "../values.js";
10
11
  import { runtimeTag } from "./package.js";
@@ -69,7 +70,7 @@ export function runPackageV1(pkg, options = {}) {
69
70
  trace.reject(rejection);
70
71
  return trace.finish();
71
72
  }
72
- const vm = new ExecutorV1(pkg, trace, StdinReader.fromReplay(replay.inputs), options.schedulerPolicy ?? "round_robin");
73
+ const vm = new ExecutorV1(pkg, trace, StdinReader.fromReplay(replay.inputs), options.schedulerPolicy ?? "round_robin", options);
73
74
  try {
74
75
  vm.run();
75
76
  }
@@ -85,6 +86,7 @@ class ExecutorV1 {
85
86
  pkg;
86
87
  trace;
87
88
  stdin;
89
+ options;
88
90
  current;
89
91
  host;
90
92
  scheduler;
@@ -103,10 +105,11 @@ class ExecutorV1 {
103
105
  running = false;
104
106
  rootComplete = false;
105
107
  rootFault = null;
106
- constructor(pkg, trace, stdin, policy) {
108
+ constructor(pkg, trace, stdin, policy, options) {
107
109
  this.pkg = pkg;
108
110
  this.trace = trace;
109
111
  this.stdin = stdin;
112
+ this.options = options;
110
113
  this.scheduler = new FrameScheduler(trace, policy);
111
114
  this.ids = new DeterministicIds(trace.replay.seed);
112
115
  this.host = {
@@ -171,7 +174,8 @@ class ExecutorV1 {
171
174
  const act = this.current;
172
175
  while (act.opIndex < act.block.ops.length) {
173
176
  const op = act.block.ops[act.opIndex++];
174
- this.commitStep();
177
+ if (op.op !== "coverage.hit")
178
+ this.commitStep();
175
179
  try {
176
180
  this.execute(act, op);
177
181
  }
@@ -350,6 +354,11 @@ class ExecutorV1 {
350
354
  // ── instructions ─────────────────────────────────────────────────────────
351
355
  execute(act, op) {
352
356
  switch (op.op) {
357
+ case "coverage.hit": {
358
+ const key = String(op.counter);
359
+ this.trace.coverage[key] = (this.trace.coverage[key] ?? 0) + 1;
360
+ return;
361
+ }
353
362
  case "const.int":
354
363
  this.define(act, op.dst, {
355
364
  kind: "i64",
@@ -404,9 +413,19 @@ class ExecutorV1 {
404
413
  act.places.set(op.place, { value: UNIT, valid: false });
405
414
  return;
406
415
  case "store.init":
407
- case "store.assign":
408
416
  writeRef(this.placeRef(act, op.place), this.read(act, op.value));
409
417
  return;
418
+ case "store.assign": {
419
+ const ref = this.placeRef(act, op.place);
420
+ const replacement = this.read(act, op.value);
421
+ const previous = liveRef(ref) ? readRef(ref) : UNIT;
422
+ // Retire the old owner before invoking user close code. Publish the
423
+ // replacement after cleanup, including when close records a fault;
424
+ // the following cleanup.dispatch then drains the new owner once.
425
+ invalidateRef(ref);
426
+ this.releaseOperation(act, previous, () => writeRef(ref, replacement));
427
+ return;
428
+ }
410
429
  case "load.copy":
411
430
  this.define(act, op.dst, cloneValue(readRef(this.placeRef(act, op.place))));
412
431
  return;
@@ -634,6 +653,106 @@ class ExecutorV1 {
634
653
  this.define(act, op.dst, { kind: "generator", id });
635
654
  return;
636
655
  }
656
+ case "task.race": {
657
+ const scope = act.scopes.get(op.scope);
658
+ if (!scope)
659
+ throw new Error("race has no task scope");
660
+ const members = op.members.map((operand) => {
661
+ const member = this.taskFor(this.read(act, operand));
662
+ this.invalidate(act, operand);
663
+ if (member.grouped)
664
+ throw new Error("race reuses a task handle");
665
+ member.grouped = true;
666
+ return member;
667
+ });
668
+ if (!members.length)
669
+ throw new Error("race requires members");
670
+ const task = this.newTask();
671
+ scope.tasks.push(task);
672
+ let selected = false;
673
+ let winner = null;
674
+ let value = UNIT;
675
+ let failure = null;
676
+ let cancelled = false;
677
+ const losersCancelled = new Set();
678
+ const ready = () => {
679
+ if (task.done || task.finishing)
680
+ return;
681
+ if (!selected) {
682
+ const first = members
683
+ .filter((member) => member.done)
684
+ .sort((a, b) => a.completedAt - b.completedAt)[0];
685
+ if (!first)
686
+ return;
687
+ selected = true;
688
+ if (!first.fault) {
689
+ winner = first;
690
+ value = first.value;
691
+ first.value = UNIT;
692
+ }
693
+ }
694
+ if (!cancelled) {
695
+ cancelled = true;
696
+ for (const member of members)
697
+ if (!member.done) {
698
+ losersCancelled.add(member);
699
+ member.cancel();
700
+ }
701
+ }
702
+ if (members.some((member) => !member.done))
703
+ return;
704
+ task.finishing = true;
705
+ for (const member of members) {
706
+ const index = member.changed.indexOf(ready);
707
+ if (index >= 0)
708
+ member.changed.splice(index, 1);
709
+ }
710
+ for (const member of [...members].sort((a, b) => a.completedAt - b.completedAt)) {
711
+ if (member.fault &&
712
+ !(member.fault.kind === "panic" &&
713
+ member.fault.cancelled &&
714
+ losersCancelled.has(member)))
715
+ failure = combineFaults(failure, member.fault);
716
+ member.fault = null;
717
+ }
718
+ const losers = members
719
+ .filter((member) => member !== winner)
720
+ .map((member) => {
721
+ const result = member.value;
722
+ member.value = UNIT;
723
+ return result;
724
+ });
725
+ this.closeValueAsync({ kind: "vector", elementType: "race result", items: losers }, this.faultText(failure), (closeFault) => {
726
+ failure = combineFaults(failure, closeFault);
727
+ const finish = (releaseFault = null) => {
728
+ failure = combineFaults(failure, releaseFault);
729
+ this.settleTask(task, failure ? UNIT : value, failure);
730
+ for (const changed of [...scope.changed])
731
+ changed();
732
+ };
733
+ if (failure && winner)
734
+ this.closeValueAsync(value, this.faultText(failure), finish, act.context.actor);
735
+ else
736
+ finish();
737
+ }, act.context.actor);
738
+ };
739
+ task.cancel = () => {
740
+ if (task.done || task.finishing)
741
+ return;
742
+ failure ??= {
743
+ kind: "panic",
744
+ message: "task cancelled",
745
+ cancelled: true,
746
+ };
747
+ selected = true;
748
+ ready();
749
+ };
750
+ for (const member of members)
751
+ member.changed.push(ready);
752
+ this.define(act, op.dst, { kind: "task", id: task.id });
753
+ ready();
754
+ return;
755
+ }
637
756
  case "task.spawn": {
638
757
  const group = act.scopes.get(op.scope);
639
758
  if (!group)
@@ -656,7 +775,9 @@ class ExecutorV1 {
656
775
  if (!fault || (fault.kind === "panic" && fault.cancelled))
657
776
  fault = closeFault ?? fault;
658
777
  this.settleTask(task, value, fault);
659
- if (fault && !(fault.kind === "panic" && fault.cancelled)) {
778
+ if (!task.grouped &&
779
+ fault &&
780
+ !(fault.kind === "panic" && fault.cancelled)) {
660
781
  for (const sibling of group.tasks)
661
782
  if (!sibling.done)
662
783
  sibling.cancel();
@@ -783,6 +904,8 @@ class ExecutorV1 {
783
904
  ...term.args.map((operand) => this.boundary(act, operand)),
784
905
  ];
785
906
  this.current = this.activate(this.functionAt(entry.callee), args, act, term.result, term.normal, term.unwind);
907
+ if (entry.receiver_handback)
908
+ this.current.erasedHandback = receiver;
786
909
  return;
787
910
  }
788
911
  case "call": {
@@ -866,6 +989,11 @@ class ExecutorV1 {
866
989
  this.callShim(act, term, shim);
867
990
  return;
868
991
  }
992
+ case "wire.codec": {
993
+ const args = term.args.map((operand) => this.boundary(act, operand));
994
+ this.runValueProgram(act, term, args, executeCodec(this.pkg, term, args[0], this.options.codecBridge, this.options.propertyInput ?? []));
995
+ return;
996
+ }
869
997
  case "value.call": {
870
998
  const args = term.args.map((operand) => this.boundary(act, operand));
871
999
  this.runValueProgram(act, term, args, selectedValue(this.pkg, term.plan, args));
@@ -960,8 +1088,10 @@ class ExecutorV1 {
960
1088
  return;
961
1089
  case "unreachable":
962
1090
  throw new Error(`${act.fn.name}: reached an unreachable endpoint`);
963
- default:
964
- throw new Error(`terminator ${term.op} has no sequential executor`);
1091
+ default: {
1092
+ const exhaustive = term;
1093
+ throw new Error(`unhandled terminator: ${String(exhaustive)}`);
1094
+ }
965
1095
  }
966
1096
  }
967
1097
  takeEdge(act, edge) {
@@ -992,6 +1122,7 @@ class ExecutorV1 {
992
1122
  }
993
1123
  catch (error) {
994
1124
  if (error instanceof ShimFault) {
1125
+ this.keepInputs(act, term, args);
995
1126
  this.raiseFault(act, { kind: "trap", trap: error.trap, message: error.message }, term.unwind);
996
1127
  return;
997
1128
  }
@@ -999,6 +1130,20 @@ class ExecutorV1 {
999
1130
  }
1000
1131
  this.completeShim(act, term, value);
1001
1132
  }
1133
+ /// A runtime call whose contract keeps its inputs on failure fails before
1134
+ /// taking them, so each moved argument stays with the caller, whose unwind
1135
+ /// edge stores it back.
1136
+ keepsInputs(term) {
1137
+ return term.op === "runtime.call" && term.keeps_inputs_on_failure === true;
1138
+ }
1139
+ keepInputs(act, term, args) {
1140
+ if (!this.keepsInputs(term))
1141
+ return;
1142
+ term.args.forEach((operand, index) => {
1143
+ if (operand.decision === "move")
1144
+ this.define(act, operand.value, args[index]);
1145
+ });
1146
+ }
1002
1147
  runValueProgram(act, term, args, iterator) {
1003
1148
  this.running = false;
1004
1149
  let initial = true;
@@ -1010,10 +1155,16 @@ class ExecutorV1 {
1010
1155
  this.scheduler.enqueue(act.context.id, () => this.runFrame(act));
1011
1156
  };
1012
1157
  const fail = (fault, extra = UNIT) => {
1013
- const owned = args.filter((_arg, index) => ["move", "copy", "snapshot"].includes(term.args[index].decision));
1158
+ const kept = this.keepsInputs(term) ? ["move"] : [];
1159
+ const owned = args.filter((_arg, index) => {
1160
+ const decision = term.args[index].decision;
1161
+ return (["move", "copy", "snapshot"].includes(decision) &&
1162
+ !kept.includes(decision));
1163
+ });
1014
1164
  this.closeValueAsync({ kind: "vector", elementType: "", items: [...owned, extra] }, this.faultText(fault), (closeFault) => {
1015
1165
  if (fault.kind === "panic" && fault.cancelled && closeFault)
1016
1166
  fault = closeFault;
1167
+ this.keepInputs(act, term, args);
1017
1168
  this.raiseFault(act, fault, term.unwind);
1018
1169
  resume();
1019
1170
  });
@@ -1116,6 +1267,13 @@ class ExecutorV1 {
1116
1267
  if (!act.normal) {
1117
1268
  throw new Error(`${act.fn.name}: returned from a call with a "never" result`);
1118
1269
  }
1270
+ if (act.erasedHandback) {
1271
+ if (value.kind !== "record" || value.fields.length !== 2) {
1272
+ throw new Error(`${act.fn.name}: invalid erased receiver dual return`);
1273
+ }
1274
+ act.erasedHandback.value = value.fields[1];
1275
+ value = value.fields[0];
1276
+ }
1119
1277
  // The result is defined on the normal edge, like a block parameter, so it
1120
1278
  // is in scope for that edge's arguments.
1121
1279
  if (act.result && act.result !== "never") {
@@ -1166,12 +1324,15 @@ class ExecutorV1 {
1166
1324
  }
1167
1325
  // A failing `var self` method hands its receiver back, defined on the
1168
1326
  // unwind edge like a result on the normal edge.
1169
- if ((handback === null) !== !act.handback) {
1327
+ if ((handback === null) !== !(act.handback || act.erasedHandback)) {
1170
1328
  throw new Error(`${act.fn.name}: receiver handback disagrees with its call`);
1171
1329
  }
1172
1330
  if (handback !== null && act.handback) {
1173
1331
  this.define(caller, act.handback.value, handback);
1174
1332
  }
1333
+ if (handback !== null && act.erasedHandback) {
1334
+ act.erasedHandback.value = handback;
1335
+ }
1175
1336
  caller.fault = fault;
1176
1337
  this.current = caller;
1177
1338
  this.takeEdge(caller, act.unwind);
@@ -1305,11 +1466,10 @@ class ExecutorV1 {
1305
1466
  return;
1306
1467
  }
1307
1468
  const failure = group.deadline ??
1308
- group.tasks.find((task) => task.fault &&
1469
+ group.tasks.find((task) => !task.grouped &&
1470
+ task.fault &&
1309
1471
  !(task.fault.kind === "panic" && task.fault.cancelled))?.fault;
1310
- wake(UNIT, mode === "propagate_fault" || mode === "cancel_losers_after_fault"
1311
- ? null
1312
- : (failure ?? null));
1472
+ wake(UNIT, mode === "propagate_fault" ? null : (failure ?? null));
1313
1473
  };
1314
1474
  // Cancellation requests descendant cleanup; it cannot bypass the
1315
1475
  // join barrier and let the parent retire their captured resources.
@@ -1380,7 +1540,12 @@ class ExecutorV1 {
1380
1540
  return;
1381
1541
  }
1382
1542
  case "NativeIo": {
1383
- // Admission passed only `StdinReadLine`. The record is the raw line,
1543
+ if (term.detail?.operation === "SinkFinish") {
1544
+ this.host.pipes.close(this.boundary(act, term.inputs[0]));
1545
+ this.park(act, term)(UNIT);
1546
+ return;
1547
+ }
1548
+ // Admission passed `StdinReadLine`. The record is the raw line,
1384
1549
  // so the records concatenate to the stdin the program consumed.
1385
1550
  const line = this.stdin.readLine() ?? new Uint8Array();
1386
1551
  this.trace.recordReplayInput({ kind: "stdin", data: new TextDecoder().decode(line) }, false);
@@ -1608,6 +1773,7 @@ class ExecutorV1 {
1608
1773
  newTask() {
1609
1774
  const task = {
1610
1775
  id: this.ids.task(),
1776
+ grouped: false,
1611
1777
  done: false,
1612
1778
  finishing: false,
1613
1779
  value: UNIT,
@@ -1715,6 +1881,7 @@ class ExecutorV1 {
1715
1881
  fields: layout.state_fields.map((field) => field.deferred ? UNIT : (args[supplied++] ?? UNIT)),
1716
1882
  },
1717
1883
  mailbox: [],
1884
+ disposals: [],
1718
1885
  admission: [],
1719
1886
  busy: true,
1720
1887
  alive: true,
@@ -1792,6 +1959,7 @@ class ExecutorV1 {
1792
1959
  handler,
1793
1960
  payload: fields,
1794
1961
  reply: false,
1962
+ obligated: true,
1795
1963
  complete: (_value, error, drained) => {
1796
1964
  if (error)
1797
1965
  this.closeValue(payload, error);
@@ -1871,13 +2039,13 @@ class ExecutorV1 {
1871
2039
  }
1872
2040
  const full = actor.layout.mailbox_capacity !== undefined &&
1873
2041
  actor.mailbox.length >= actor.layout.mailbox_capacity;
1874
- // The destination's declaration answers a full queue before the
1875
- // sender's policy: a coalescing mailbox supersedes the queued
1876
- // message with the same key, then its fallback or a declared
1877
- // `drop_new` / `drop_old` resolves the rest. Either loss reports a
1878
- // discard; only a declaration that refuses reaches the sender.
1879
- // A discard completes in this turn unless an authored close
1880
- // suspends: the sender keeps its turn, as it does natively.
2042
+ // Declared loss transfers cleanup to the receiver. Sender-selected
2043
+ // loss still closes the sender's unadmitted request in its own frame.
2044
+ const discardAtReceiver = (value, report) => {
2045
+ actor.disposals.push(value);
2046
+ this.dispatchActor(actor);
2047
+ finish(report);
2048
+ };
1881
2049
  const discard = (value, report) => {
1882
2050
  let settled = false;
1883
2051
  let waiting = false;
@@ -1900,7 +2068,7 @@ class ExecutorV1 {
1900
2068
  if (declared === "coalesce" && coalesce) {
1901
2069
  const key = coalesce.keys.find((key) => key.message === handler.message_id);
1902
2070
  const queued = key
1903
- ? actor.mailbox.findIndex((message) => !message.reply &&
2071
+ ? actor.mailbox.findIndex((message) => !message.obligated &&
1904
2072
  message.handler.message_id === handler.message_id &&
1905
2073
  equals(message.payload[key.param], payload.fields[key.param]))
1906
2074
  : -1;
@@ -1910,19 +2078,19 @@ class ExecutorV1 {
1910
2078
  ...superseded,
1911
2079
  payload: payload.fields,
1912
2080
  };
1913
- discard({ kind: "record", typeId: "", fields: superseded.payload }, accepted(true));
2081
+ discardAtReceiver({ kind: "record", typeId: "", fields: superseded.payload }, accepted(false));
1914
2082
  return;
1915
2083
  }
1916
2084
  declared = coalesce.fallback;
1917
2085
  }
1918
2086
  if (declared === "drop_new") {
1919
- discard(payload, accepted(true));
2087
+ discardAtReceiver(payload, accepted(true));
1920
2088
  return;
1921
2089
  }
1922
2090
  // A queued completion call owes its caller a reply, so `drop_old`
1923
2091
  // evicts only a one-way message and refuses when there is none.
1924
2092
  const oldest = declared === "drop_old"
1925
- ? actor.mailbox.findIndex((message) => !message.reply)
2093
+ ? actor.mailbox.findIndex((message) => !message.obligated)
1926
2094
  : -1;
1927
2095
  if (oldest >= 0) {
1928
2096
  const [evicted] = actor.mailbox.splice(oldest, 1);
@@ -1930,10 +2098,11 @@ class ExecutorV1 {
1930
2098
  handler,
1931
2099
  payload: payload.fields,
1932
2100
  reply: false,
2101
+ obligated: false,
1933
2102
  complete: (_value, _error, drained) => drained?.(null),
1934
2103
  });
1935
2104
  this.dispatchActor(actor);
1936
- discard({ kind: "record", typeId: "", fields: evicted.payload }, accepted(false));
2105
+ discardAtReceiver({ kind: "record", typeId: "", fields: evicted.payload }, accepted(false));
1937
2106
  return;
1938
2107
  }
1939
2108
  if (full) {
@@ -1958,6 +2127,7 @@ class ExecutorV1 {
1958
2127
  handler,
1959
2128
  payload: payload.fields,
1960
2129
  reply: false,
2130
+ obligated: false,
1961
2131
  complete: (_value, _error, drained) => drained?.(null),
1962
2132
  });
1963
2133
  this.trace.snapshot("actor.send", {
@@ -2252,7 +2422,13 @@ class ExecutorV1 {
2252
2422
  }
2253
2423
  admitted = true;
2254
2424
  receiver = actor;
2255
- actor.mailbox.push({ handler, payload, complete: finish, reply: true });
2425
+ actor.mailbox.push({
2426
+ handler,
2427
+ payload,
2428
+ complete: finish,
2429
+ reply: true,
2430
+ obligated: true,
2431
+ });
2256
2432
  this.trace.snapshot("actor.ask", {
2257
2433
  actor_id: actor.id,
2258
2434
  handler: handler.name,
@@ -2272,6 +2448,18 @@ class ExecutorV1 {
2272
2448
  dispatchActor(actor) {
2273
2449
  if (actor.busy || !actor.alive || actor.drainingMailbox)
2274
2450
  return;
2451
+ if (actor.disposals.length > 0) {
2452
+ actor.busy = true;
2453
+ const values = actor.disposals.splice(0);
2454
+ this.closeValueAsync({ kind: "vector", elementType: "message", items: values }, this.faultText(actor.crashing), (fault) => {
2455
+ actor.busy = false;
2456
+ if (fault)
2457
+ this.crashActor(actor, fault);
2458
+ else
2459
+ this.dispatchActor(actor);
2460
+ }, actor);
2461
+ return;
2462
+ }
2275
2463
  if (actor.terminating && actor.mailbox.length > 0) {
2276
2464
  this.drainMailbox(actor, this.faultText(actor.closingFault), (fault) => {
2277
2465
  actor.closingFault = combineFaults(actor.closingFault, fault);
@@ -2649,11 +2837,14 @@ class ExecutorV1 {
2649
2837
  this.closeValueAsync({
2650
2838
  kind: "vector",
2651
2839
  elementType: "message",
2652
- items: messages.map((message) => ({
2653
- kind: "record",
2654
- typeId: "",
2655
- fields: message.payload,
2656
- })),
2840
+ items: [
2841
+ ...actor.disposals.splice(0),
2842
+ ...messages.map((message) => ({
2843
+ kind: "record",
2844
+ typeId: "",
2845
+ fields: message.payload,
2846
+ })),
2847
+ ],
2657
2848
  }, fault, (closeFault) => {
2658
2849
  actor.drainingMailbox = false;
2659
2850
  for (const message of messages)
@@ -2906,8 +3097,6 @@ function sendErrorRole(reason) {
2906
3097
  return "SendErrorCancelled";
2907
3098
  case "VersionMismatch":
2908
3099
  return "SendErrorVersionMismatch";
2909
- case "Unauthorized":
2910
- return "SendErrorUnauthorized";
2911
3100
  case "Backpressure":
2912
3101
  return "SendErrorBackpressure";
2913
3102
  case "Dead":
@@ -3104,6 +3293,21 @@ function applyBinary(op, lhs, rhs, ty) {
3104
3293
  return { kind: "bool", value: truth(lhs) && truth(rhs) };
3105
3294
  case "Or":
3106
3295
  return { kind: "bool", value: truth(lhs) || truth(rhs) };
3296
+ case "BitAnd":
3297
+ case "BitOr":
3298
+ case "BitXor":
3299
+ // Both operands were already evaluated; bool operands stay bool.
3300
+ if (lhs.kind === "bool" && rhs.kind === "bool") {
3301
+ return {
3302
+ kind: "bool",
3303
+ value: op === "BitAnd"
3304
+ ? lhs.value && rhs.value
3305
+ : op === "BitOr"
3306
+ ? lhs.value || rhs.value
3307
+ : lhs.value !== rhs.value,
3308
+ };
3309
+ }
3310
+ break;
3107
3311
  default:
3108
3312
  break;
3109
3313
  }
@@ -34,7 +34,7 @@ export interface VariantShape {
34
34
  cases: VariantCase[];
35
35
  runtime_tags?: Partial<Record<RuntimeVariantRole, number>>;
36
36
  }
37
- export type RuntimeVariantRole = "OptionSome" | "OptionNone" | "ResultOk" | "ResultErr" | "ActorErrorRejected" | "ActorErrorFailed" | "ActorErrorTrapped" | "ActorErrorDead" | "ActorErrorTimedOut" | "ActorErrorNodeNotRunning" | "ActorErrorRoutingFailed" | "ActorErrorEncodeFailed" | "ActorErrorConnectionDropped" | "ActorErrorPartition" | "SendErrorFull" | "SendErrorClosed" | "SendErrorPartition" | "SendErrorStaleRef" | "SendErrorLocalShutdown" | "SendErrorCancelled" | "SendErrorVersionMismatch" | "SendErrorUnauthorized" | "SendErrorBackpressure" | "SendErrorDead" | "DeliveryAccepted" | "DeliveryDiscarded" | "LinkErrorNoContext";
37
+ export type RuntimeVariantRole = "OptionSome" | "OptionNone" | "ResultOk" | "ResultErr" | "ActorErrorRejected" | "ActorErrorFailed" | "ActorErrorTrapped" | "ActorErrorDead" | "ActorErrorTimedOut" | "ActorErrorNodeNotRunning" | "ActorErrorRoutingFailed" | "ActorErrorEncodeFailed" | "ActorErrorConnectionDropped" | "ActorErrorPartition" | "SendErrorFull" | "SendErrorClosed" | "SendErrorPartition" | "SendErrorStaleRef" | "SendErrorLocalShutdown" | "SendErrorCancelled" | "SendErrorVersionMismatch" | "SendErrorBackpressure" | "SendErrorDead" | "SendErrorWriteTimedOut" | "DeliveryAccepted" | "DeliveryDiscarded" | "LinkErrorNoContext";
38
38
  export declare function runtimeTag(shape: VariantShape, role: RuntimeVariantRole): number;
39
39
  export interface RuntimeFamilyEntry {
40
40
  id: number;
@@ -44,6 +44,7 @@ export interface RuntimeFamilyEntry {
44
44
  export interface ExternEntry {
45
45
  id: number;
46
46
  symbol: string;
47
+ native_capability?: string;
47
48
  }
48
49
  export type PlaceDecl = {
49
50
  id: number;
@@ -75,11 +76,20 @@ export type PlaceDecl = {
75
76
  };
76
77
  export interface ValueCapability {
77
78
  id: number;
78
- capability: "Eq" | "Hash";
79
+ capability: "Eq" | "Hash" | "Less" | "LessEqual" | "Greater" | "GreaterEqual";
79
80
  ty: string;
80
81
  callable?: number;
81
- components?: number[];
82
- variants?: number[][];
82
+ components?: ValueComponentCapabilities[];
83
+ variants?: ValueComponentCapabilities[][];
84
+ }
85
+ export interface ValueComponentCapabilities {
86
+ selected: number;
87
+ ordering?: {
88
+ less: number;
89
+ less_equal: number;
90
+ greater: number;
91
+ greater_equal: number;
92
+ };
83
93
  }
84
94
  export interface ClosureShape {
85
95
  id: number;
@@ -93,6 +103,7 @@ export interface VtableShape {
93
103
  method: string;
94
104
  callee: number;
95
105
  receiver: BoundaryDecision;
106
+ receiver_handback?: boolean;
96
107
  }>;
97
108
  }
98
109
  export interface FunctionV1 {
@@ -115,6 +126,9 @@ interface OpBase {
115
126
  span?: SpanV1 | null;
116
127
  }
117
128
  export type OpV1 = (OpBase & {
129
+ op: "coverage.hit";
130
+ counter: number;
131
+ }) | (OpBase & {
118
132
  op: "const.int";
119
133
  dst: number;
120
134
  value: string;
@@ -341,6 +355,11 @@ export type OpV1 = (OpBase & {
341
355
  }) | (OpBase & {
342
356
  op: "task_scope.close";
343
357
  scope: number;
358
+ }) | (OpBase & {
359
+ op: "task.race";
360
+ dst: number;
361
+ scope: number;
362
+ members: Operand[];
344
363
  }) | (OpBase & {
345
364
  op: "task.spawn";
346
365
  dst: number;
@@ -414,12 +433,25 @@ export type TermV1 = (TermBase & {
414
433
  }) | (TermBase & CallShape & {
415
434
  op: "value.call";
416
435
  plan: number;
436
+ }) | (TermBase & CallShape & {
437
+ op: "wire.codec";
438
+ codec: number;
439
+ format: number;
440
+ direction: "encode" | "decode" | "test_input";
441
+ decoded: {
442
+ shape: number;
443
+ ok: number;
444
+ error: number;
445
+ error_root: number;
446
+ } | null;
447
+ callbacks: Record<string, number[]>;
417
448
  }) | (TermBase & CallShape & {
418
449
  op: "runtime.call";
419
450
  family: number;
420
451
  structural?: number | null;
421
452
  callbacks?: number[];
422
453
  releases_contents?: boolean;
454
+ keeps_inputs_on_failure?: boolean;
423
455
  result_member_shapes?: Array<number | null>;
424
456
  result_shape: number | null;
425
457
  }) | (TermBase & CallShape & {
@@ -436,10 +468,6 @@ export type TermV1 = (TermBase & {
436
468
  };
437
469
  result_shape: number | null;
438
470
  error_shape: number | null;
439
- }) | (TermBase & CallShape & {
440
- op: "wire.codec";
441
- direction: string;
442
- plan: number;
443
471
  }) | (TermBase & {
444
472
  op: "panic";
445
473
  message: BoundaryOperand;
@@ -498,7 +526,7 @@ export interface EntryPlan {
498
526
  export interface ActorProtocol {
499
527
  actor: number;
500
528
  message: number;
501
- policy: "reject" | "wait" | "drop_newest" | "replace_latest";
529
+ policy: "reject" | "wait" | "drop_newest";
502
530
  sealed: boolean;
503
531
  }
504
532
  export type ActorOperation = {
@@ -558,7 +586,6 @@ export interface ActorShape {
558
586
  keys: Array<{
559
587
  message: number;
560
588
  param: number;
561
- kind: string;
562
589
  }>;
563
590
  };
564
591
  max_heap_bytes?: number;
@@ -582,6 +609,7 @@ export interface SupervisorShape {
582
609
  }>;
583
610
  }
584
611
  export interface PackageV1 {
612
+ codecs?: string[];
585
613
  resources?: Array<{
586
614
  kind: string;
587
615
  ty?: string;