@effect-agent/platform-cloudflare 0.1.0-beta.117 → 0.1.0-beta.119

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.
@@ -1540,50 +1540,13 @@ export declare const encodeHostResponse: (input: AbortRecorded | ApprovalRecorde
1540
1540
  } | {
1541
1541
  readonly _tag: "HostFailed";
1542
1542
  readonly failure: {
1543
- readonly _tag: "HostProtocolError";
1544
- readonly message: string;
1545
- } | {
1546
- readonly _tag: "AgentInputError";
1547
- readonly message: string;
1548
- } | {
1549
- readonly _tag: "DigestError";
1543
+ readonly _tag: "DurableAlarmError";
1544
+ readonly operation: string;
1550
1545
  readonly message: string;
1551
1546
  readonly cause?: Schema.Json | undefined;
1552
1547
  } | {
1553
- readonly _tag: "AdmissionConflict";
1548
+ readonly _tag: "ThreadNotMaterialized";
1554
1549
  readonly threadId: string;
1555
- readonly principal: string;
1556
- readonly idempotencyKey: string;
1557
- readonly existingInputDigest: string;
1558
- readonly attemptedInputDigest: string;
1559
- } | {
1560
- readonly _tag: "AdmissionPolicyError";
1561
- readonly reason: "occupied" | "refused" | "unavailable";
1562
- readonly code: string;
1563
- readonly cause?: import("effect-agent/failure-diagnostic").Diagnostic | undefined;
1564
- readonly stack?: string | undefined;
1565
- } | {
1566
- readonly _tag: "SettlementConflict";
1567
- readonly submissionId: string;
1568
- readonly existingOutcome: "aborted" | "completed" | "failed";
1569
- } | {
1570
- readonly _tag: "ApprovalConflict";
1571
- readonly submissionId: string;
1572
- readonly toolCallId: string;
1573
- readonly existingDecision: "approved" | "denied";
1574
- } | {
1575
- readonly _tag: "UnknownResolutionConflict";
1576
- readonly submissionId: string;
1577
- readonly toolCallId: string;
1578
- } | {
1579
- readonly _tag: "JoinedToHost";
1580
- readonly submissionId: string;
1581
- readonly hostSubmissionId: string;
1582
- } | {
1583
- readonly _tag: "LedgerError";
1584
- readonly operation: string;
1585
- readonly message: string;
1586
- readonly cause?: import("effect-agent/failure-diagnostic").Diagnostic | undefined;
1587
1550
  } | {
1588
1551
  readonly _tag: "ThreadStoreError";
1589
1552
  readonly operation: string;
@@ -1597,8 +1560,10 @@ export declare const encodeHostResponse: (input: AbortRecorded | ApprovalRecorde
1597
1560
  readonly sequence?: number | undefined;
1598
1561
  } | undefined;
1599
1562
  } | {
1600
- readonly _tag: "ThreadNotMaterialized";
1601
- readonly threadId: string;
1563
+ readonly _tag: "LedgerError";
1564
+ readonly operation: string;
1565
+ readonly message: string;
1566
+ readonly cause?: import("effect-agent/failure-diagnostic").Diagnostic | undefined;
1602
1567
  } | {
1603
1568
  readonly _tag: "AppendConflict";
1604
1569
  readonly threadId: string;
@@ -1611,25 +1576,60 @@ export declare const encodeHostResponse: (input: AbortRecorded | ApprovalRecorde
1611
1576
  readonly threadId: string;
1612
1577
  readonly actualEpoch: number;
1613
1578
  readonly attemptedEpoch: number;
1579
+ } | {
1580
+ readonly _tag: "OperationDenied";
1581
+ readonly operation: "abort" | "awaitSettlement" | "explain" | "observe" | "resolveApproval" | "resolveUnknown" | "retry" | "scanObligations" | "verify" | "wake";
1582
+ readonly reason: string;
1583
+ readonly threadId?: string | undefined;
1584
+ readonly submissionId?: string | undefined;
1585
+ } | {
1586
+ readonly _tag: "AdmissionConflict";
1587
+ readonly threadId: string;
1588
+ readonly principal: string;
1589
+ readonly idempotencyKey: string;
1590
+ readonly existingInputDigest: string;
1591
+ readonly attemptedInputDigest: string;
1592
+ } | {
1593
+ readonly _tag: "AdmissionPolicyError";
1594
+ readonly reason: "occupied" | "refused" | "unavailable";
1595
+ readonly code: string;
1596
+ readonly cause?: import("effect-agent/failure-diagnostic").Diagnostic | undefined;
1597
+ readonly stack?: string | undefined;
1598
+ } | {
1599
+ readonly _tag: "AgentInputError";
1600
+ readonly message: string;
1601
+ } | {
1602
+ readonly _tag: "DigestError";
1603
+ readonly message: string;
1604
+ readonly cause?: Schema.Json | undefined;
1614
1605
  } | {
1615
1606
  readonly _tag: "DurableRuntimeFailpointError";
1616
1607
  readonly location: "abort:after-intent" | "approval:after-request-append" | "approval:after-suspend" | "checkpoint:after-save" | "checkpoint:before-save" | "claim:after-claim" | "compaction:after-canonical-append" | "compaction:before-canonical-append" | "input:after-canonical-append" | "join:after-canonical-append" | "join:after-claim" | "policy:after-reservation-append" | "policy:before-reservation-append" | "resolve:after-intent" | "run:after-duration-append" | "run:after-start-append" | "run:before-duration-append" | "run:before-start-append" | "step:after-step-append" | "subagent:after-admit" | "subagent:after-child-abort-intent" | "subagent:after-child-ready" | "subagent:after-join-append" | "subagent:after-release" | "subagent:after-release-pending" | "subagent:after-request-append" | "subagent:after-reserve" | "subagent:after-sibling-settle" | "subagent:after-start-append" | "subagent:after-suspend" | "subagent:before-join-append" | "submit:after-admit" | "submit:after-materialize" | "terminalize:after-canonical-append" | "terminalize:after-reserve" | "tools:after-prepared-append" | "tools:after-unavailable-append" | "tools:before-prepared-append" | "tools:before-unavailable-append" | "turn:after-canonical-append" | "turn:after-response-append" | "turn:after-results-append" | "update:after-canonical-append" | "update:after-delivery-insert" | "update:before-canonical-append" | "update:before-delivery-insert" | "worker:after-completion-append" | "worker:after-origin-append" | "worker:after-report-append" | "worker:after-report-delivery" | "worker:after-source-append" | "worker:after-stop-append" | "worker:after-stop-seal" | "worker:after-subtree-append" | "worker:before-completion-append" | "worker:before-origin-append" | "worker:before-report-append" | "worker:before-report-delivery" | "worker:before-source-append" | "worker:before-stop-append" | "worker:before-stop-seal" | "worker:before-subtree-append";
1608
+ } | {
1609
+ readonly _tag: "SettlementConflict";
1610
+ readonly submissionId: string;
1611
+ readonly existingOutcome: "aborted" | "completed" | "failed";
1612
+ } | {
1613
+ readonly _tag: "JoinedToHost";
1614
+ readonly submissionId: string;
1615
+ readonly hostSubmissionId: string;
1617
1616
  } | {
1618
1617
  readonly _tag: "AdmissionLimitExceeded";
1619
1618
  readonly limit: "database-bytes" | "input-bytes" | "queue-depth";
1620
1619
  readonly actual: number;
1621
1620
  readonly maximum: number;
1622
1621
  } | {
1623
- readonly _tag: "DurableAlarmError";
1624
- readonly operation: string;
1622
+ readonly _tag: "HostProtocolError";
1625
1623
  readonly message: string;
1626
- readonly cause?: Schema.Json | undefined;
1627
1624
  } | {
1628
- readonly _tag: "OperationDenied";
1629
- readonly operation: "abort" | "awaitSettlement" | "explain" | "observe" | "resolveApproval" | "resolveUnknown" | "retry" | "scanObligations" | "verify" | "wake";
1630
- readonly reason: string;
1631
- readonly threadId?: string | undefined;
1632
- readonly submissionId?: string | undefined;
1625
+ readonly _tag: "ApprovalConflict";
1626
+ readonly submissionId: string;
1627
+ readonly toolCallId: string;
1628
+ readonly existingDecision: "approved" | "denied";
1629
+ } | {
1630
+ readonly _tag: "UnknownResolutionConflict";
1631
+ readonly submissionId: string;
1632
+ readonly toolCallId: string;
1633
1633
  };
1634
1634
  }, Schema.SchemaError, never>;
1635
1635
  export declare const decodeHostResponse: (input: unknown, options?: import("effect/SchemaAST").ParseOptions) => Effect.Effect<AbortRecorded | ApprovalRecorded | HostFailed | ObservedPage | ProgressCancelled | ProgressObserved | SettlementReached | SubmissionStatusResponse | SubmitSucceeded | UnknownResolutionRecorded, Schema.SchemaError, never>;
@@ -9,6 +9,8 @@ declare const ProtectedTransportError_base: Schema.Class<ProtectedTransportError
9
9
  declare class ProtectedTransportError extends ProtectedTransportError_base {}
10
10
  declare const ProtectedBrowserDispatch_base: Context.ServiceClass<ProtectedBrowserDispatch, "@effect-agent/platform-cloudflare/ProtectedBrowserDispatch", {
11
11
  readonly mark: Effect.Effect<void>;
12
+ /** Only an acknowledged reply before assignment may restore the preceding dispatch evidence. */
13
+ readonly confirmNoWrite: Effect.Effect<void>;
12
14
  }>;
13
15
  /** Per-write evidence authority, provided by the policy immediately around a transport fill. */
14
16
  declare class ProtectedBrowserDispatch extends ProtectedBrowserDispatch_base {}
@@ -96,7 +98,10 @@ interface BrowserRunProtectedSession {
96
98
  readonly getHandoffState: Effect.Effect<BrowserRunHandoffState, ProtectedBrowserError>;
97
99
  /** Read only the exact page’s current origin after its recorded handoff completes; tools remain paused. */
98
100
  readonly getReturnOrigin: Effect.Effect<typeof CredentialOrigin.Type, ProtectedBrowserError>;
99
- /** Resume a committed pause; human handoffs also require provider completion. Observe before acting. */
101
+ /**
102
+ * Resume a committed pause; human handoffs also require provider completion. Returns no page data.
103
+ * A fresh authorized observation must succeed before page actions or credential operations.
104
+ */
100
105
  readonly returnControl: Effect.Effect<void, ProtectedBrowserError>;
101
106
  /** Release this attachment only after committing its suspended checkpoint; preserve the remote browser. */
102
107
  readonly detach: Effect.Effect<void, ProtectedBrowserError>;
@@ -173,7 +173,7 @@ const makeProtectedBrowserPolicy = Effect.fn("ProtectedBrowser.open")(function*
173
173
  const error = cause.reasons.find((reason) => reason._tag === "Fail" && Schema.is(ProtectedBrowserError)(reason.error));
174
174
  const reason = unexpected.length === 0 && error?._tag === "Fail" && Schema.is(ProtectedBrowserError)(error.error) ? error.error.reason : "provider";
175
175
  const interrupt = cause.reasons.some((reason) => reason._tag === "Interrupt");
176
- if (dispatch !== "not-dispatched" || interrupt || reason === "provider" || reason === "timeout") yield* close;
176
+ if (dispatch === "possibly-dispatched" || dispatch === "dispatched" && reason !== "busy" || interrupt || reason === "provider" || reason === "timeout") yield* close;
177
177
  if (interrupt) return yield* Effect.interrupt;
178
178
  const outcome = fail(dispatch === "possibly-dispatched" && reason === "provider" ? "outcome-unknown" : reason);
179
179
  return yield* unexpected.length > 0 ? reportedBrowserError(outcome) : inheritBrowserReport(error?._tag === "Fail" ? error.error : void 0, outcome);
@@ -267,9 +267,15 @@ const makeProtectedBrowserPolicy = Effect.fn("ProtectedBrowser.open")(function*
267
267
  target: control.target,
268
268
  role: control.role
269
269
  });
270
- yield* publicFailure(driver.fill(decoded.ref, control.role, Redacted.make(decoded.value))).pipe(Effect.provideService(ProtectedBrowserDispatch, { mark: Effect.sync(() => {
271
- dispatch = "possibly-dispatched";
272
- }) }));
270
+ const previousDispatch = dispatch;
271
+ yield* publicFailure(driver.fill(decoded.ref, control.role, Redacted.make(decoded.value))).pipe(Effect.provideService(ProtectedBrowserDispatch, {
272
+ mark: Effect.sync(() => {
273
+ dispatch = "possibly-dispatched";
274
+ }),
275
+ confirmNoWrite: Effect.sync(() => {
276
+ dispatch = previousDispatch;
277
+ })
278
+ }));
273
279
  dispatch = "dispatched";
274
280
  milestone = "filled";
275
281
  yield* permitObservation;
@@ -356,11 +362,17 @@ const makeProtectedBrowserPolicy = Effect.fn("ProtectedBrowser.open")(function*
356
362
  yield* authorize;
357
363
  const value = secretFor(material, field.role);
358
364
  if (value === void 0) return yield* fail("missing-credential");
365
+ const previousDispatch = dispatch;
359
366
  observation = "protected";
360
- yield* publicFailure(driver.fill(field.ref, field.role, value)).pipe(Effect.provideService(ProtectedBrowserDispatch, { mark: Effect.sync(() => {
361
- dispatch = "possibly-dispatched";
362
- if (!exposures.some((target) => sameTarget(target, offer.target))) exposures.push(offer.target);
363
- }) }));
367
+ yield* publicFailure(driver.fill(field.ref, field.role, value)).pipe(Effect.provideService(ProtectedBrowserDispatch, {
368
+ mark: Effect.sync(() => {
369
+ dispatch = "possibly-dispatched";
370
+ if (!exposures.some((target) => sameTarget(target, offer.target))) exposures.push(offer.target);
371
+ }),
372
+ confirmNoWrite: Effect.sync(() => {
373
+ dispatch = previousDispatch;
374
+ })
375
+ }));
364
376
  dispatch = "dispatched";
365
377
  milestone = "partial-fill";
366
378
  }
@@ -422,7 +434,6 @@ const makeProtectedBrowserPolicy = Effect.fn("ProtectedBrowser.open")(function*
422
434
  returnControl: locked(Effect.gen(function* () {
423
435
  if (detached || observation === "closed") return yield* fail("closed");
424
436
  if (!suspended) return yield* fail("denied");
425
- yield* permitObservation;
426
437
  offers.clear();
427
438
  driver.resetReferences();
428
439
  needsObservation = true;
@@ -505,12 +516,26 @@ const inspectFrame = `(() => {
505
516
  } else if (el instanceof HTMLButtonElement || (el instanceof HTMLInputElement && ['submit','button'].includes(type))) {
506
517
  if (!el.matches(':disabled')) role = type === 'submit' && form ? 'submit' : type === 'button' ? 'button' : 'unsupported';
507
518
  } else if (el instanceof HTMLAnchorElement) role = 'link';
519
+ const options = [];
520
+ let truncated = false;
521
+ if (role === 'select') {
522
+ for (let index = 0; index < el.options.length; index++) {
523
+ if (index === 256) { truncated = true; break; }
524
+ const option = el.options[index];
525
+ const label = option.label.trim();
526
+ // Never publish a shortened label as an exact fill choice.
527
+ if (label.length > 200) { truncated = true; continue; }
528
+ options.push({label, selected: option.selected, disabled: option.matches(':disabled')});
529
+ }
530
+ }
508
531
  const fingerprint = JSON.stringify([role, action, method, enctype, name, completion, type, choiceValue]);
509
532
  return { role, formIndex, action, fingerprint, ...(isChoice(el) ? {checked: el.checked} : {}),
533
+ ...(role === 'select' ? {options, truncated} : {}),
510
534
  label: (labelText(el) ?? (nativeField ? '' : el.textContent) ?? '').slice(0,200) };
511
535
  };
512
- const expose = ({role, formIndex, action, label, checked}) =>
513
- ({role, formIndex, action, label, ...(checked === undefined ? {} : {checked})});
536
+ const expose = ({role, formIndex, action, label, checked, options, truncated}) =>
537
+ ({role, formIndex, action, label, ...(checked === undefined ? {} : {checked}),
538
+ ...(options === undefined ? {} : {options, truncated})});
514
539
  const original = elements.map(describe);
515
540
  const validate = (index) => {
516
541
  const current = describe(elements[index]);
@@ -540,13 +565,13 @@ const inspectFrame = `(() => {
540
565
  const current = validate(index);
541
566
  if (!current || current.role !== role) return false;
542
567
  const el = elements[index];
543
- if (role === 'select') {
568
+ if (el instanceof HTMLSelectElement) {
544
569
  const options = [...el.options].filter(option => !option.matches(':disabled'));
545
- const values = options.filter(option => option.value === value);
546
- const matches = values.length > 0 ? values : options.filter(option => option.textContent?.trim() === value);
547
- if (matches.length !== 1) return 'unsupported';
570
+ // Ordinary fills name the visible choice; credential material stays a private value.
571
+ const matches = options.filter(option => (role === 'select' ? option.label.trim() : option.value) === value);
572
+ if (matches.length !== 1) return 'unsupported-before-write';
548
573
  value = matches[0].value;
549
- if ([...el.options].filter(option => option.value === value).length !== 1) return 'unsupported';
574
+ if ([...el.options].filter(option => option.value === value).length !== 1) return 'unsupported-before-write';
550
575
  }
551
576
  let prototype = Object.getPrototypeOf(el);
552
577
  let setter;
@@ -576,9 +601,17 @@ const Description = Schema.Struct({
576
601
  formIndex: Schema.Natural,
577
602
  action: Schema.String.check(Schema.isMaxLength(maxAttributeLength)),
578
603
  label: Schema.String.check(Schema.isMaxLength(200)),
579
- checked: Schema.optionalKey(Schema.Boolean)
604
+ checked: Schema.optionalKey(Schema.Boolean),
605
+ options: ProtectedBrowserControl.fields.options,
606
+ truncated: Schema.optionalKey(Schema.Boolean)
580
607
  });
581
608
  const Descriptions = Schema.Array(Schema.NullOr(Description)).check(Schema.isMaxLength(65));
609
+ const FillResult = Schema.Literals([
610
+ true,
611
+ false,
612
+ "unsupported-before-write",
613
+ "unsupported"
614
+ ]);
582
615
  /** One host-private acquired session. The SDK handles and exact-session cleanup have one owner. */
583
616
  var ProtectedNativeSession = class extends Context.Service()("@effect-agent/platform-cloudflare/ProtectedNativeSession") {};
584
617
  const transportError = (reason) => new ProtectedTransportError({ reason });
@@ -749,6 +782,7 @@ const makeProtectedNativeTransport = Effect.fn("ProtectedNativeTransport.make")(
749
782
  for (let index = 0; index < descriptions.length; index++) {
750
783
  const desc = descriptions[index];
751
784
  if (!desc) continue;
785
+ if (desc.truncated) truncated = true;
752
786
  if (discovered.length === 64) {
753
787
  truncated = true;
754
788
  break;
@@ -765,6 +799,7 @@ const makeProtectedNativeTransport = Effect.fn("ProtectedNativeTransport.make")(
765
799
  role: desc.role,
766
800
  label: desc.label,
767
801
  ...desc.checked === void 0 ? {} : { checked: desc.checked },
802
+ ...desc.options === void 0 ? {} : { options: desc.options },
768
803
  ...desc.role === "link" ? { url: desc.action } : {},
769
804
  target: CredentialTarget.make({
770
805
  topOrigin: before.topOrigin,
@@ -806,7 +841,7 @@ const makeProtectedNativeTransport = Effect.fn("ProtectedNativeTransport.make")(
806
841
  const dispatch = yield* ProtectedBrowserDispatch;
807
842
  const state = yield* get(ref);
808
843
  yield* dispatch.mark;
809
- const filled = yield* remote("protected.fill", () => state.frame.handle.evaluate((held, index, expectedRole, secret) => {
844
+ const raw = yield* remote("protected.fill", () => state.frame.handle.evaluate((held, index, expectedRole, secret) => {
810
845
  if (typeof held !== "object" || held === null) return false;
811
846
  return Reflect.apply(Reflect.get(held, "fill"), held, [
812
847
  index,
@@ -814,8 +849,12 @@ const makeProtectedNativeTransport = Effect.fn("ProtectedNativeTransport.make")(
814
849
  secret
815
850
  ]);
816
851
  }, state.index, role, Redacted.value(value)));
852
+ const filled = yield* decode(FillResult, raw);
853
+ if (filled === false || filled === "unsupported-before-write") {
854
+ yield* dispatch.confirmNoWrite;
855
+ return yield* transportError(filled === false ? "stale-reference" : "unsupported");
856
+ }
817
857
  if (filled === "unsupported") return yield* transportError("unsupported");
818
- if (filled !== true) return yield* transportError("stale-reference");
819
858
  }),
820
859
  click: Effect.fn("ProtectedNativeTransport.click")(function* (ref) {
821
860
  const state = yield* get(ref);