agentfootprint-lens 0.34.0 → 0.36.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.
- package/README.md +161 -0
- package/dist/{chunk-ZSOYNKUI.js → chunk-EGU4GZVH.js} +220 -2
- package/dist/chunk-EGU4GZVH.js.map +1 -0
- package/dist/core.cjs +231 -0
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +27 -1
- package/dist/{index-CCGJxlZc.d.cts → index-BLBZ1BHq.d.cts} +455 -2
- package/dist/{index-CCGJxlZc.d.ts → index-BLBZ1BHq.d.ts} +455 -2
- package/dist/index.cjs +3610 -2786
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +483 -27
- package/dist/index.d.ts +483 -27
- package/dist/index.js +3260 -2654
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZSOYNKUI.js.map +0 -1
package/README.md
CHANGED
|
@@ -436,6 +436,131 @@ prose via the default humanizer.
|
|
|
436
436
|
|
|
437
437
|
---
|
|
438
438
|
|
|
439
|
+
## Answer a paused run with a real component (typed HITL)
|
|
440
|
+
|
|
441
|
+
agentfootprint 9.24 taught every human-ask door (`askHuman`, the `ask`
|
|
442
|
+
middleware, `defineTool({ checkInComponent })`) to carry an optional typed
|
|
443
|
+
half beside the prose question: `{ componentId, props?, propsRef? }`. The id
|
|
444
|
+
names a component **your screen registered** — ids + props, never markup the
|
|
445
|
+
model wrote, the same no-eval law artifacts follow. Small props ride the ask
|
|
446
|
+
inline; the big half (a 200-option picker's options) rides the **artifact
|
|
447
|
+
store** as a `propsRef` claim ticket, redeemed through the same resolver and
|
|
448
|
+
the same session identity as every other artifact.
|
|
449
|
+
|
|
450
|
+
`<AwaitingPane>` is the screen's half:
|
|
451
|
+
|
|
452
|
+
```tsx
|
|
453
|
+
import {
|
|
454
|
+
AwaitingPane,
|
|
455
|
+
decisionRequestBody,
|
|
456
|
+
httpArtifactResolver,
|
|
457
|
+
registerDecisionComponent,
|
|
458
|
+
} from 'agentfootprint-lens';
|
|
459
|
+
|
|
460
|
+
// 1. Teach Lens your collectors, once at startup. ('option-picker' ships.)
|
|
461
|
+
registerDecisionComponent({
|
|
462
|
+
componentId: 'refund-form',
|
|
463
|
+
component: ({ question, props, data, respond }) => (
|
|
464
|
+
<MyRefundForm limits={props} rows={data} onSubmit={(answer) => respond(answer)} />
|
|
465
|
+
),
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// 2. When a reply comes back awaiting, render the pane. The lens does NOT
|
|
469
|
+
// own the POST — your app does; decisionRequestBody formats the body.
|
|
470
|
+
const resolver = httpArtifactResolver({ url: '/invoke', sessionId });
|
|
471
|
+
<AwaitingPane
|
|
472
|
+
awaiting={reply.awaiting}
|
|
473
|
+
resolver={resolver}
|
|
474
|
+
onDecision={(decision) =>
|
|
475
|
+
fetch('/invoke', {
|
|
476
|
+
method: 'POST',
|
|
477
|
+
headers: { 'content-type': 'application/json' },
|
|
478
|
+
body: JSON.stringify(decisionRequestBody({ decision, sessionId })),
|
|
479
|
+
})
|
|
480
|
+
}
|
|
481
|
+
/>
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
The person clicks; `onDecision` receives **the structured decision** — the
|
|
485
|
+
option's id, or the approve/decline record (`approveDecision` /
|
|
486
|
+
`declineDecision`, byte-compatible with agentfootprint's check-in
|
|
487
|
+
vocabulary). After answering, the pane renders the decision as one sentence —
|
|
488
|
+
*"Approved by alice@ops — 'verified'."* — which is **display only**: the
|
|
489
|
+
structured decision is the record; the words are a rendering of it.
|
|
490
|
+
|
|
491
|
+
Nothing on this road dead-ends the human. An unknown `componentId` falls back
|
|
492
|
+
to the prose question plus a plain answer box and *says so*; an expired
|
|
493
|
+
`propsRef` renders the honest placeholder plus the answer box; a crashed
|
|
494
|
+
registered component is caught and stated; a consent gate (`checkIn` /
|
|
495
|
+
middleware `ask`) falls back to Approve/Decline with a required "deciding as"
|
|
496
|
+
field, because an audit record with no actor names nobody.
|
|
497
|
+
|
|
498
|
+
The headless half is on `agentfootprint-lens/core` — `readAwaitingComponent`
|
|
499
|
+
(era-robust: reads `awaiting.component` and the three `pauseData` homes),
|
|
500
|
+
`approveDecision` / `declineDecision`, `decisionRequestBody`,
|
|
501
|
+
`decisionSentence`, `isConsentAsk` — so a Vue or CLI shell can build the same
|
|
502
|
+
pane.
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## Scrubbing by group — the active group is a named place
|
|
507
|
+
|
|
508
|
+
One causal trace, replayed at two zoom levels. On the **per-step** ruler each
|
|
509
|
+
commit is a stop; on the **grouped** ruler each boundary is a stop, and ◀ ▶ moves
|
|
510
|
+
a whole group at a time.
|
|
511
|
+
|
|
512
|
+
The chart used to paint both the same way, and that was wrong for the second one.
|
|
513
|
+
A stage's styling is by TYPE — the LLM call carries a hero emphasis (accent
|
|
514
|
+
border, tint, glow) and the cursor's one node pulses — so landing on a group of
|
|
515
|
+
six nodes pulled the eye to the LLM box. The group, the thing the ruler had
|
|
516
|
+
actually moved by, never read as the position at all.
|
|
517
|
+
|
|
518
|
+
Pass `granularity="group"` and it does:
|
|
519
|
+
|
|
520
|
+
- **one accent for every member.** An LLM call, a tool and a context pill light
|
|
521
|
+
identically — same tint, same intensity. What a node IS stays legible in its
|
|
522
|
+
icon and its shape; how loud it is no longer depends on its type.
|
|
523
|
+
- **everything else recedes uniformly** — one dim, not a second ranking.
|
|
524
|
+
- **a boundary is drawn around the members**, from their real measured positions,
|
|
525
|
+
with the group's **name** on its top edge. Scrubbing group to group animates it
|
|
526
|
+
(and doesn't, under `prefers-reduced-motion: reduce`).
|
|
527
|
+
|
|
528
|
+
The name is `groupDisplayName` — the same spelling the WHAT HAPPENED boundary
|
|
529
|
+
list uses. One place, one name.
|
|
530
|
+
|
|
531
|
+
```tsx
|
|
532
|
+
<Lens recorder={recorder} runner={runner} granularity="group" />
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
Or, in a shell that owns its own canvas and its own cursor:
|
|
536
|
+
|
|
537
|
+
```tsx
|
|
538
|
+
import { LensFlow, useChartGroup } from 'agentfootprint-lens';
|
|
539
|
+
|
|
540
|
+
function WhyLensChart({ recorder, chart, cursorCommitIdx }) {
|
|
541
|
+
// The group the cursor stands in, as chart node ids. Derived from the
|
|
542
|
+
// boundary ranges the grouped ruler already computes its stops from —
|
|
543
|
+
// no extra fetch, no second cursor.
|
|
544
|
+
const group = useChartGroup(recorder, cursorCommitIdx);
|
|
545
|
+
return <LensFlow chart={chart} granularity="group" activeGroup={group} />;
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
`granularity` defaults to `'step'`, and on that path nothing changes: no classes,
|
|
550
|
+
no boundary, the same chart the Flow Lens has always drawn. `'group'` at a commit
|
|
551
|
+
no boundary encloses also renders as `'step'` — a mode with nothing to draw draws
|
|
552
|
+
nothing, rather than boxing the whole chart.
|
|
553
|
+
|
|
554
|
+
Restyle it with one variable (`--lens-group-accent`, falling back to
|
|
555
|
+
`--fp-group-accent`), or target the classes directly: `.lens-group-node--member`,
|
|
556
|
+
`.lens-group-node--outsider`, `.lens-group-boundary`, `.lens-group-boundary-name`.
|
|
557
|
+
|
|
558
|
+
Headless: `activeChartGroup({ groups, commits, commitIdx })` on
|
|
559
|
+
`agentfootprint-lens/core` is the pure function behind the hook — same answer for
|
|
560
|
+
a Vue or CLI shell.
|
|
561
|
+
|
|
562
|
+
---
|
|
563
|
+
|
|
439
564
|
## Theming
|
|
440
565
|
|
|
441
566
|
**Lens inherits theme tokens from your app via CSS variables.** Set `--fp-*`
|
|
@@ -547,6 +672,25 @@ graph in one call. Returns an unsubscribe. Call it once per run.
|
|
|
547
672
|
| `chart` | `LensFlowProps['chart']?` | Render YOUR graph instead of the derived one. |
|
|
548
673
|
| `stepGraph` | `StepGraph?` | Bring your own step graph; by default Lens uses the recorder's. |
|
|
549
674
|
| `toolChoice` | `ToolChoiceSource?` | Mount the per-iteration tool-choice panel. |
|
|
675
|
+
| `granularity` | `'step' \| 'group'?` | Which ruler is scrubbing the chart. `'group'` paints the cursor's group as a named place. Default `'step'`. See [Scrubbing by group](#scrubbing-by-group--the-active-group-is-a-named-place). |
|
|
676
|
+
|
|
677
|
+
### `<LensFlow>` — the chart canvas on its own
|
|
678
|
+
|
|
679
|
+
The chart without the shell, for consumers who own their layout and their
|
|
680
|
+
cursor. Takes `chart`, the runtime overlay, the cursor, and — for the grouped
|
|
681
|
+
ruler — `granularity="group"` plus `activeGroup` (from `useChartGroup`). Every
|
|
682
|
+
other prop is unchanged by group mode.
|
|
683
|
+
|
|
684
|
+
### `useChartGroup(recorder, commitIdx, options?)` / `activeChartGroup(...)`
|
|
685
|
+
|
|
686
|
+
The group the cursor stands in, resolved to CHART NODE IDS: the boundary's
|
|
687
|
+
commit range read off the recording, each commit's `runtimeStageId` stripped of
|
|
688
|
+
its `#executionIndex` (the id rule every chart-click and co-active highlight in
|
|
689
|
+
the ecosystem already uses), plus the group's own mount. Returns `undefined`
|
|
690
|
+
when no boundary encloses the cursor. `options.includeRoot` opts into the
|
|
691
|
+
synthetic Run root, which is off by default because a box around the whole chart
|
|
692
|
+
states nothing. `activeChartGroup` is the pure, React-free twin on
|
|
693
|
+
`agentfootprint-lens/core`.
|
|
550
694
|
|
|
551
695
|
### `observeRecording(recording, options?)`
|
|
552
696
|
|
|
@@ -613,6 +757,23 @@ The headless half — `httpArtifactResolver`, `storeArtifactResolver`,
|
|
|
613
757
|
`agentfootprint-lens/core`. See
|
|
614
758
|
[Render an artifact by its ref](#render-an-artifact-by-its-ref).
|
|
615
759
|
|
|
760
|
+
### `<AwaitingPane>` — answer a paused run with a real component
|
|
761
|
+
|
|
762
|
+
Takes the `awaiting` payload a served agent replied with (`awaiting`), an
|
|
763
|
+
optional `ArtifactResolver` (`resolver`, only needed when the ask ships a
|
|
764
|
+
`propsRef`), and `onDecision` — which receives the person's STRUCTURED
|
|
765
|
+
decision; your app posts it (`decisionRequestBody` formats the wire body).
|
|
766
|
+
Renders the component registered for the ask's `componentId`
|
|
767
|
+
(`registerDecisionComponent({ componentId, component })`) with `{ question,
|
|
768
|
+
props, data, respond }`; `'option-picker'` ships as a built-in. Unknown id,
|
|
769
|
+
expired ref, failed door and crashed component all state themselves and fall
|
|
770
|
+
back to a live answer surface — never a dead end. After answering it renders
|
|
771
|
+
the decision as one sentence (display only; the structured decision is the
|
|
772
|
+
record). The headless half — `readAwaitingComponent`, `approveDecision` /
|
|
773
|
+
`declineDecision`, `decisionRequestBody`, `decisionSentence`, `isConsentAsk`
|
|
774
|
+
— is on `agentfootprint-lens/core`. See
|
|
775
|
+
[Answer a paused run with a real component](#answer-a-paused-run-with-a-real-component-typed-hitl).
|
|
776
|
+
|
|
616
777
|
### Headless core
|
|
617
778
|
|
|
618
779
|
`agentfootprint-lens/core` is React-free: `LensRecorder`, `ChangeNotifier`,
|
|
@@ -2296,6 +2296,126 @@ function selectToolChoiceCall(calls, cursorRuntimeStageId, cursorKind) {
|
|
|
2296
2296
|
return within ?? prev;
|
|
2297
2297
|
}
|
|
2298
2298
|
|
|
2299
|
+
// src/core/group/groupDisplayName.ts
|
|
2300
|
+
function groupDisplayName(label) {
|
|
2301
|
+
const named = label.subflowName ?? label.compositionName ?? label.primitiveKind;
|
|
2302
|
+
if (named !== void 0 && named !== "") return named;
|
|
2303
|
+
if (label.type === "run.entry") return "Run";
|
|
2304
|
+
return label.runtimeStageId;
|
|
2305
|
+
}
|
|
2306
|
+
function groupDisplayNameForLabel(label) {
|
|
2307
|
+
return groupDisplayName(label);
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// src/core/group/Group.ts
|
|
2311
|
+
function groupContainsCommit(group, commitIdx) {
|
|
2312
|
+
if (commitIdx < group.opensAtCommitIdx) return false;
|
|
2313
|
+
if (group.closesAtCommitIdx === void 0) return true;
|
|
2314
|
+
return commitIdx <= group.closesAtCommitIdx;
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
// src/core/group/activeChartGroup.ts
|
|
2318
|
+
function chartNodeIdOf(runtimeStageId) {
|
|
2319
|
+
const hash = runtimeStageId.lastIndexOf("#");
|
|
2320
|
+
return hash < 0 ? runtimeStageId : runtimeStageId.slice(0, hash);
|
|
2321
|
+
}
|
|
2322
|
+
function activeChartGroup(args) {
|
|
2323
|
+
const { groups, commits, commitIdx, includeRoot = false } = args;
|
|
2324
|
+
if (!Number.isFinite(commitIdx) || commitIdx < 0) return void 0;
|
|
2325
|
+
let best;
|
|
2326
|
+
for (const group of groups) {
|
|
2327
|
+
if (group.isRoot && !includeRoot) continue;
|
|
2328
|
+
if (!groupContainsCommit(group, commitIdx)) continue;
|
|
2329
|
+
if (best === void 0) {
|
|
2330
|
+
best = group;
|
|
2331
|
+
continue;
|
|
2332
|
+
}
|
|
2333
|
+
if (group.depth > best.depth) best = group;
|
|
2334
|
+
else if (group.depth === best.depth && group.opensAtCommitIdx >= best.opensAtCommitIdx) best = group;
|
|
2335
|
+
}
|
|
2336
|
+
if (best === void 0) return void 0;
|
|
2337
|
+
const memberNodeIds = /* @__PURE__ */ new Set();
|
|
2338
|
+
memberNodeIds.add(chartNodeIdOf(best.runtimeGroupId));
|
|
2339
|
+
const from = Math.max(0, best.opensAtCommitIdx);
|
|
2340
|
+
const to = Math.min(commits.length - 1, best.closesAtCommitIdx ?? commits.length - 1);
|
|
2341
|
+
for (let i = from; i <= to; i++) {
|
|
2342
|
+
const rid = commits[i]?.runtimeStageId;
|
|
2343
|
+
if (rid === void 0 || rid === "") continue;
|
|
2344
|
+
memberNodeIds.add(chartNodeIdOf(rid));
|
|
2345
|
+
}
|
|
2346
|
+
return {
|
|
2347
|
+
runtimeGroupId: best.runtimeGroupId,
|
|
2348
|
+
name: best.name,
|
|
2349
|
+
memberNodeIds,
|
|
2350
|
+
opensAtCommitIdx: best.opensAtCommitIdx,
|
|
2351
|
+
closesAtCommitIdx: best.closesAtCommitIdx,
|
|
2352
|
+
depth: best.depth
|
|
2353
|
+
};
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
// src/core/group/buildGroups.ts
|
|
2357
|
+
function samePath(a, b) {
|
|
2358
|
+
return a.length === b.length && a.every((s, i) => s === b[i]);
|
|
2359
|
+
}
|
|
2360
|
+
function buildGroups(boundaryIndex) {
|
|
2361
|
+
const all = boundaryIndex.overlapping(0, Number.MAX_SAFE_INTEGER);
|
|
2362
|
+
if (all.length === 0) return [];
|
|
2363
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2364
|
+
const result = [];
|
|
2365
|
+
for (let i = 0; i < all.length; i++) {
|
|
2366
|
+
const entry = all[i];
|
|
2367
|
+
const label = entry.label;
|
|
2368
|
+
if (seen.has(label.runtimeStageId)) continue;
|
|
2369
|
+
seen.add(label.runtimeStageId);
|
|
2370
|
+
let parentGroupId;
|
|
2371
|
+
if (label.type === "subflow.entry") {
|
|
2372
|
+
const enclosing = boundaryIndex.enclosing(entry.startIdx);
|
|
2373
|
+
const parentPath = label.subflowPath.slice(0, -1);
|
|
2374
|
+
for (let j = enclosing.length - 1; j >= 0; j--) {
|
|
2375
|
+
const cand = enclosing[j].label;
|
|
2376
|
+
if (cand.runtimeStageId === label.runtimeStageId) continue;
|
|
2377
|
+
if (samePath(cand.subflowPath, parentPath)) {
|
|
2378
|
+
parentGroupId = cand.runtimeStageId;
|
|
2379
|
+
break;
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
if (parentGroupId === void 0) {
|
|
2383
|
+
for (let j = enclosing.length - 1; j >= 0; j--) {
|
|
2384
|
+
const cand = enclosing[j].label;
|
|
2385
|
+
if (cand.runtimeStageId === label.runtimeStageId) continue;
|
|
2386
|
+
parentGroupId = cand.runtimeStageId;
|
|
2387
|
+
break;
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
} else if (label.type === "composition.start") {
|
|
2391
|
+
const enclosing = boundaryIndex.enclosing(entry.startIdx);
|
|
2392
|
+
for (let j = enclosing.length - 1; j >= 0; j--) {
|
|
2393
|
+
const cand = enclosing[j].label;
|
|
2394
|
+
if (cand.runtimeStageId === label.runtimeStageId) continue;
|
|
2395
|
+
parentGroupId = cand.runtimeStageId;
|
|
2396
|
+
break;
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
const isRoot = label.type === "run.entry";
|
|
2400
|
+
const name = groupDisplayNameForLabel(label);
|
|
2401
|
+
const compositionKind = label.type === "composition.start" ? label.compositionKind : void 0;
|
|
2402
|
+
result.push({
|
|
2403
|
+
runtimeGroupId: label.runtimeStageId,
|
|
2404
|
+
name,
|
|
2405
|
+
parentGroupId,
|
|
2406
|
+
subflowPath: label.subflowPath,
|
|
2407
|
+
depth: label.depth,
|
|
2408
|
+
opensAtCommitIdx: entry.startIdx,
|
|
2409
|
+
closesAtCommitIdx: entry.endIdx,
|
|
2410
|
+
isRoot,
|
|
2411
|
+
...compositionKind !== void 0 ? { compositionKind } : {},
|
|
2412
|
+
...label.slotKind !== void 0 ? { slotKind: label.slotKind } : {},
|
|
2413
|
+
...label.primitiveKind !== void 0 ? { primitiveKind: label.primitiveKind } : {}
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2416
|
+
return result;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2299
2419
|
// src/core/translate/helpers/makeNodeId.ts
|
|
2300
2420
|
function makeRootNodeId(kind, id) {
|
|
2301
2421
|
return `${kind.toLowerCase()}:${id}`;
|
|
@@ -3367,6 +3487,91 @@ function artifactPlaceholder(presented) {
|
|
|
3367
3487
|
return `${describePresented(presented)} \u2014 expired; re-run to regenerate.`;
|
|
3368
3488
|
}
|
|
3369
3489
|
|
|
3490
|
+
// src/core/hitl/decision.ts
|
|
3491
|
+
function readAwaitingComponent(awaiting) {
|
|
3492
|
+
if (typeof awaiting !== "object" || awaiting === null) return void 0;
|
|
3493
|
+
const pending = awaiting;
|
|
3494
|
+
const direct = asComponent(pending.component);
|
|
3495
|
+
if (direct) return direct;
|
|
3496
|
+
const bag = pending.pauseData;
|
|
3497
|
+
if (typeof bag !== "object" || bag === null) return void 0;
|
|
3498
|
+
const homes = bag;
|
|
3499
|
+
return asComponent(homes.component) ?? asComponent(
|
|
3500
|
+
typeof homes.checkIn === "object" && homes.checkIn !== null ? homes.checkIn.component : void 0
|
|
3501
|
+
) ?? asComponent(
|
|
3502
|
+
typeof homes.ask === "object" && homes.ask !== null ? homes.ask.component : void 0
|
|
3503
|
+
);
|
|
3504
|
+
}
|
|
3505
|
+
function asComponent(value) {
|
|
3506
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
3507
|
+
const c = value;
|
|
3508
|
+
if (typeof c.componentId !== "string" || c.componentId.length === 0) return void 0;
|
|
3509
|
+
return value;
|
|
3510
|
+
}
|
|
3511
|
+
function isConsentAsk(awaiting) {
|
|
3512
|
+
if (typeof awaiting !== "object" || awaiting === null) return false;
|
|
3513
|
+
const pending = awaiting;
|
|
3514
|
+
return typeof pending.checkIn === "object" && pending.checkIn !== null || typeof pending.ask === "object" && pending.ask !== null;
|
|
3515
|
+
}
|
|
3516
|
+
function approveDecision(input) {
|
|
3517
|
+
return buildConsent(true, input, "approveDecision");
|
|
3518
|
+
}
|
|
3519
|
+
function declineDecision(input) {
|
|
3520
|
+
return buildConsent(false, input, "declineDecision");
|
|
3521
|
+
}
|
|
3522
|
+
function buildConsent(approved, input, door) {
|
|
3523
|
+
if (typeof input?.by !== "string" || input.by.trim().length === 0) {
|
|
3524
|
+
throw new Error(
|
|
3525
|
+
`${door} needs \`by\` \u2014 who is deciding (an operator id, an email). The decision is an audit record, and a record with no actor names nobody; it cannot be defaulted.`
|
|
3526
|
+
);
|
|
3527
|
+
}
|
|
3528
|
+
return {
|
|
3529
|
+
approved,
|
|
3530
|
+
by: input.by,
|
|
3531
|
+
at: Date.now(),
|
|
3532
|
+
...input.note !== void 0 && input.note.length > 0 && { note: input.note }
|
|
3533
|
+
};
|
|
3534
|
+
}
|
|
3535
|
+
function decisionRequestBody(args) {
|
|
3536
|
+
if (args === null || typeof args !== "object" || !("decision" in args)) {
|
|
3537
|
+
throw new Error(
|
|
3538
|
+
"decisionRequestBody needs `decision` \u2014 the structured answer to the pending ask. Its presence is what distinguishes a resume from a new message on the wire."
|
|
3539
|
+
);
|
|
3540
|
+
}
|
|
3541
|
+
if (args.decision === void 0) {
|
|
3542
|
+
throw new Error(
|
|
3543
|
+
"decisionRequestBody refused `decision: undefined` \u2014 JSON drops the key, the wire would read a NEW MESSAGE, and the pause would sit unanswered while the screen believed it answered. Post the actual decision (`null` is allowed; absence is not)."
|
|
3544
|
+
);
|
|
3545
|
+
}
|
|
3546
|
+
return {
|
|
3547
|
+
input: args.input ?? "",
|
|
3548
|
+
decision: args.decision,
|
|
3549
|
+
...args.sessionId !== void 0 && { sessionId: args.sessionId }
|
|
3550
|
+
};
|
|
3551
|
+
}
|
|
3552
|
+
var SENTENCE_JSON_CAP = 120;
|
|
3553
|
+
function decisionSentence(decision) {
|
|
3554
|
+
if (isConsentDecision(decision)) {
|
|
3555
|
+
const verb = decision.approved ? "Approved" : "Declined";
|
|
3556
|
+
const note = decision.note !== void 0 ? ` \u2014 "${decision.note}"` : "";
|
|
3557
|
+
return `${verb} by ${decision.by}${note}.`;
|
|
3558
|
+
}
|
|
3559
|
+
if (typeof decision === "string") return `Answered: "${decision}".`;
|
|
3560
|
+
if (decision === null || typeof decision !== "object")
|
|
3561
|
+
return `Answered: ${String(decision)}.`;
|
|
3562
|
+
let json;
|
|
3563
|
+
try {
|
|
3564
|
+
json = JSON.stringify(decision) ?? String(decision);
|
|
3565
|
+
} catch {
|
|
3566
|
+
json = "[unserializable]";
|
|
3567
|
+
}
|
|
3568
|
+
if (json.length > SENTENCE_JSON_CAP) json = `${json.slice(0, SENTENCE_JSON_CAP)}\u2026`;
|
|
3569
|
+
return `Answered with a structured decision: ${json}.`;
|
|
3570
|
+
}
|
|
3571
|
+
function isConsentDecision(value) {
|
|
3572
|
+
return typeof value === "object" && value !== null && typeof value.approved === "boolean" && typeof value.by === "string";
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3370
3575
|
export {
|
|
3371
3576
|
ChangeNotifier,
|
|
3372
3577
|
lensSnapshotRecorder,
|
|
@@ -3401,6 +3606,12 @@ export {
|
|
|
3401
3606
|
selectCommentaryAt,
|
|
3402
3607
|
selectCommentaryRanges,
|
|
3403
3608
|
selectToolChoiceCall,
|
|
3609
|
+
groupDisplayName,
|
|
3610
|
+
groupDisplayNameForLabel,
|
|
3611
|
+
groupContainsCommit,
|
|
3612
|
+
chartNodeIdOf,
|
|
3613
|
+
activeChartGroup,
|
|
3614
|
+
buildGroups,
|
|
3404
3615
|
makeRootNodeId,
|
|
3405
3616
|
makeChildNodeId,
|
|
3406
3617
|
translateAgent,
|
|
@@ -3431,6 +3642,13 @@ export {
|
|
|
3431
3642
|
readPresentedResult,
|
|
3432
3643
|
presentedFromEvents,
|
|
3433
3644
|
describePresented,
|
|
3434
|
-
artifactPlaceholder
|
|
3645
|
+
artifactPlaceholder,
|
|
3646
|
+
readAwaitingComponent,
|
|
3647
|
+
isConsentAsk,
|
|
3648
|
+
approveDecision,
|
|
3649
|
+
declineDecision,
|
|
3650
|
+
decisionRequestBody,
|
|
3651
|
+
decisionSentence,
|
|
3652
|
+
isConsentDecision
|
|
3435
3653
|
};
|
|
3436
|
-
//# sourceMappingURL=chunk-
|
|
3654
|
+
//# sourceMappingURL=chunk-EGU4GZVH.js.map
|