@wenathlan/devthink 2.0.14 → 2.0.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.
- package/CHANGELOG.md +60 -0
- package/{dist/bridge.d.ts → bridge.d.ts} +1 -1
- package/bridge.d.ts.map +1 -0
- package/bridge.js +1877 -0
- package/bun.d.ts.map +1 -0
- package/bun.js +52329 -0
- package/caps/background.json +712 -0
- package/caps/cli.json +360 -0
- package/caps/library.json +3611 -0
- package/caps/mcp.json +126 -0
- package/caps/pagebridge.json +377 -0
- package/caps/popup.json +51 -0
- package/caps/sidepanel.json +522 -0
- package/checksums.txt +142 -0
- package/cli.d.ts.map +1 -0
- package/{dist/cli.js → cli.js} +2 -2
- package/companion.d.ts.map +1 -0
- package/companion.js +389 -0
- package/crossbrowser.d.ts.map +1 -0
- package/crossbrowser.js +1191 -0
- package/dashdone.d.ts.map +1 -0
- package/dashdone.js +368 -0
- package/deno.d.ts.map +1 -0
- package/deno.js +52325 -0
- package/devthink.umd.js +52346 -0
- package/fixtures/bun.ts +7 -0
- package/fixtures/cjs.js +6 -0
- package/fixtures/deno.ts +6 -0
- package/fixtures/example-org-pagestate.json +22 -0
- package/fixtures/importers/automa-workflow.json +18 -0
- package/fixtures/importers/selenium-side.json +20 -0
- package/fixtures/importers/tabular-plan.csv +7 -0
- package/fixtures/importers/uivision-macro.json +10 -0
- package/fixtures/importers/v1-plan.json +13 -0
- package/fixtures/mcp-client.mjs +14 -0
- package/fixtures/pages/checkout.html +192 -0
- package/fixtures/pages/dashboard.html +110 -0
- package/fixtures/pages/feed.html +113 -0
- package/fixtures/pages/product-grid.html +150 -0
- package/fixtures/plans/form-inventory-plan.json +11 -0
- package/fixtures/plans/release-notes-plan.json +11 -0
- package/fixtures/recipes/agentscompete.json +11 -0
- package/fixtures/recipes/agentsforms.json +11 -0
- package/fixtures/recipes/agentsmonitor.json +11 -0
- package/fixtures/recipes/agentsreview.json +29 -0
- package/fixtures/recipes/agentswarm.json +11 -0
- package/fixtures/recipes/fillcalendar.json +12 -0
- package/fixtures/recipes/fillcheckout.json +13 -0
- package/fixtures/recipes/filldependentdropdowns.json +12 -0
- package/fixtures/recipes/fillfromcsv.json +13 -0
- package/fixtures/recipes/fillloginform.json +12 -0
- package/fixtures/recipes/fillmultistepwizard.json +13 -0
- package/fixtures/recipes/fillregistration.json +12 -0
- package/fixtures/recipes/fillsearchfilters.json +12 -0
- package/fixtures/recipes/filltypeahead.json +11 -0
- package/fixtures/recipes/monitoralerts.json +12 -0
- package/fixtures/recipes/monitoravailability.json +11 -0
- package/fixtures/recipes/monitornetworkquiet.json +12 -0
- package/fixtures/recipes/monitorpagechanges.json +11 -0
- package/fixtures/recipes/monitorpricechange.json +11 -0
- package/fixtures/recipes/monitorschedule.json +12 -0
- package/fixtures/recipes/scrapedynamicprices.json +13 -0
- package/fixtures/recipes/scrapeimagegallery.json +12 -0
- package/fixtures/recipes/scrapeinfinitefeed.json +13 -0
- package/fixtures/recipes/scrapenestedlists.json +12 -0
- package/fixtures/recipes/scrapeproductgrid.json +12 -0
- package/fixtures/recipes/scrapereviews.json +12 -0
- package/fixtures/recipes/scrapesearchresults.json +12 -0
- package/fixtures/recipes/scrapetablepagination.json +12 -0
- package/fixtures/recipes/testaccessibility.json +34 -0
- package/fixtures/recipes/testconsoleerrors.json +12 -0
- package/fixtures/recipes/testdeeplinks.json +12 -0
- package/fixtures/recipes/testformvalidation.json +13 -0
- package/fixtures/recipes/testlayoutshift.json +12 -0
- package/fixtures/recipes/testlinkcheck.json +12 -0
- package/fixtures/recipes/testloadtiming.json +12 -0
- package/fixtures/recipes/testvisualdiff.json +12 -0
- package/fixtures/workflows/changelog-digest-workflow.json +13 -0
- package/fixtures/workflows/form-survey-workflow.json +13 -0
- package/gallery.json +706 -0
- package/hardening.d.ts.map +1 -0
- package/hardening.js +188 -0
- package/headless.d.ts.map +1 -0
- package/headless.js +11522 -0
- package/http.d.ts.map +1 -0
- package/http.js +2165 -0
- package/index.cjs +54682 -0
- package/index.d.ts.map +1 -0
- package/index.js +52310 -0
- package/index.neutral.js +46343 -0
- package/{web/manifest.json → manifest.json} +1 -1
- package/mcp.d.ts.map +1 -0
- package/mcp.js +20515 -0
- package/memory.d.ts.map +1 -0
- package/{memory.ts → memory.js} +2310 -3834
- package/nativehost.template.json +10 -0
- package/node.cjs +54686 -0
- package/node.d.ts.map +1 -0
- package/pack.d.ts.map +1 -0
- package/pack.js +805 -0
- package/package.json +83 -176
- package/policy.d.ts.map +1 -0
- package/{policy.ts → policy.js} +4679 -5073
- package/progress.d.ts.map +1 -0
- package/progress.js +645 -0
- package/protocol.d.ts.map +1 -0
- package/protocol.js +8334 -0
- package/schemas/audit.schema.json +226 -0
- package/schemas/capability.schema.json +113 -0
- package/schemas/envelope.schema.json +162 -0
- package/schemas/memory.schema.json +51 -0
- package/schemas/observation.schema.json +113 -0
- package/schemas/plan.schema.json +208 -0
- package/schemas/progress.schema.json +126 -0
- package/schemas/proposal.schema.json +68 -0
- package/schemas/session.schema.json +82 -0
- package/schemas/tool.schema.json +94 -0
- package/server.d.ts.map +1 -0
- package/server.js +28877 -0
- package/umd-example.html +18 -0
- package/umd.d.ts.map +1 -0
- package/accounts.ts +0 -2087
- package/agent.ts +0 -2331
- package/antigravity.ts +0 -2139
- package/apifreeze.ts +0 -4146
- package/auth.ts +0 -642
- package/background.ts +0 -16837
- package/bridge.ts +0 -2080
- package/build.ts +0 -54
- package/bun.ts +0 -26
- package/capture.ts +0 -1614
- package/checksecrets.ts +0 -72
- package/cjs.ts +0 -9
- package/cli.ts +0 -3500
- package/commands.ts +0 -1101
- package/companion.ts +0 -450
- package/compatibility.ts +0 -37
- package/config.ts +0 -3137
- package/constants.ts +0 -943
- package/core.ts +0 -1473
- package/crossbrowser.ts +0 -1359
- package/dashdone.ts +0 -605
- package/data.ts +0 -943
- package/database.ts +0 -133
- package/debug.ts +0 -850
- package/deno.ts +0 -27
- package/devthink.ts +0 -707
- package/dist/accounts.d.ts +0 -409
- package/dist/agent.d.ts +0 -837
- package/dist/antigravity.d.ts +0 -380
- package/dist/apifreeze.d.ts +0 -113
- package/dist/auth.d.ts +0 -224
- package/dist/capture.d.ts +0 -595
- package/dist/cjs.d.ts +0 -6
- package/dist/commands.d.ts +0 -310
- package/dist/compatibility.d.ts +0 -17
- package/dist/config.d.ts +0 -1853
- package/dist/constants.d.ts +0 -637
- package/dist/core.d.ts +0 -361
- package/dist/data.d.ts +0 -330
- package/dist/database.d.ts +0 -24
- package/dist/debug.d.ts +0 -270
- package/dist/engine.d.ts +0 -74
- package/dist/environments.d.ts +0 -244
- package/dist/evidence.d.ts +0 -277
- package/dist/export.d.ts +0 -311
- package/dist/fingerprint.d.ts +0 -594
- package/dist/flow.d.ts +0 -300
- package/dist/gates.d.ts +0 -103
- package/dist/identity.d.ts +0 -51
- package/dist/ids.d.ts +0 -7
- package/dist/llm.d.ts +0 -652
- package/dist/models.d.ts +0 -1807
- package/dist/net.d.ts +0 -325
- package/dist/neutral.d.ts +0 -41
- package/dist/oauth.d.ts +0 -703
- package/dist/page.d.ts +0 -1166
- package/dist/perf.d.ts +0 -481
- package/dist/plan.d.ts +0 -134
- package/dist/project.d.ts +0 -395
- package/dist/providers.d.ts +0 -37
- package/dist/quota.d.ts +0 -453
- package/dist/recovery.d.ts +0 -621
- package/dist/request.d.ts +0 -1492
- package/dist/run.d.ts +0 -779
- package/dist/runtime.d.ts +0 -212
- package/dist/search.d.ts +0 -375
- package/dist/security.d.ts +0 -534
- package/dist/serve.d.ts +0 -136
- package/dist/session.d.ts +0 -296
- package/dist/storage.d.ts +0 -53
- package/dist/streaming.d.ts +0 -1119
- package/dist/swarm.d.ts +0 -328
- package/dist/tools.d.ts +0 -170
- package/dist/types.d.ts +0 -8357
- package/dist/utils.d.ts +0 -82
- package/dist/version.d.ts +0 -7
- package/dist/versionregistry.d.ts +0 -406
- package/dist/views.d.ts +0 -579
- package/dist/workbenchsession.d.ts +0 -69
- package/dist/workflow.d.ts +0 -649
- package/docs/readme.md +0 -92
- package/docs/schemas/antigravity.json +0 -192
- package/engine.ts +0 -1524
- package/environments.ts +0 -1005
- package/evidence.ts +0 -643
- package/export.ts +0 -889
- package/fingerprint.ts +0 -1458
- package/flow.ts +0 -1190
- package/gates.ts +0 -307
- package/gateway.ts +0 -34
- package/hardening.ts +0 -191
- package/headless.ts +0 -369
- package/http.ts +0 -2110
- package/identity.ts +0 -246
- package/ids.ts +0 -26
- package/index.ts +0 -369
- package/llm.ts +0 -2682
- package/mcp.ts +0 -1835
- package/models.ts +0 -3805
- package/modes.ts +0 -43
- package/net.ts +0 -1214
- package/neutral.ts +0 -362
- package/node.ts +0 -26
- package/oauth.ts +0 -2216
- package/offscreen.ts +0 -170
- package/pack.ts +0 -956
- package/page.ts +0 -7824
- package/pagebridge.ts +0 -1116
- package/perf.ts +0 -957
- package/plan.ts +0 -672
- package/plugin.ts +0 -76
- package/prisma.config.ts +0 -27
- package/progress.ts +0 -1308
- package/project.ts +0 -1291
- package/protocol.ts +0 -4187
- package/providers.ts +0 -246
- package/quota.ts +0 -1887
- package/recovery.ts +0 -2433
- package/request.ts +0 -4482
- package/run.ts +0 -2135
- package/runtime.ts +0 -526
- package/search.ts +0 -1760
- package/security.ts +0 -1673
- package/serve.ts +0 -419
- package/server.ts +0 -5538
- package/session.ts +0 -1162
- package/storage.ts +0 -182
- package/streaming.ts +0 -4101
- package/swarm.ts +0 -884
- package/sync.ts +0 -56
- package/tests/agentcert.test.ts +0 -128
- package/tests/agentfleet.test.ts +0 -214
- package/tests/agentreview.test.ts +0 -241
- package/tests/agentstream.test.ts +0 -335
- package/tests/agentwork.test.ts +0 -356
- package/tests/apifreeze.test.ts +0 -290
- package/tests/apimap.test.ts +0 -112
- package/tests/approvalgate.test.ts +0 -175
- package/tests/artifactmanifest.test.ts +0 -204
- package/tests/attentionfeed.test.ts +0 -163
- package/tests/backgroundruns.test.ts +0 -100
- package/tests/batchscheduling.test.ts +0 -131
- package/tests/blackboard.test.ts +0 -238
- package/tests/bridge.test.ts +0 -1088
- package/tests/browsercoverage.test.ts +0 -134
- package/tests/browserpolyfills.test.ts +0 -124
- package/tests/build.mjs +0 -1260
- package/tests/clientauth.test.ts +0 -223
- package/tests/clitools.test.ts +0 -578
- package/tests/confirmgates.test.ts +0 -190
- package/tests/containerpack.test.ts +0 -143
- package/tests/controlflow.test.ts +0 -2120
- package/tests/coordination.test.ts +0 -345
- package/tests/copyscreen.test.ts +0 -139
- package/tests/core.test.ts +0 -417
- package/tests/costcert.test.ts +0 -95
- package/tests/crossbrowser.test.ts +0 -115
- package/tests/cspaudit.test.ts +0 -79
- package/tests/dashdone.test.ts +0 -615
- package/tests/datagrid.test.ts +0 -137
- package/tests/doccheck.test.ts +0 -107
- package/tests/dombatch.test.ts +0 -109
- package/tests/emulation.test.ts +0 -282
- package/tests/environments.test.ts +0 -259
- package/tests/evidenceviews.test.ts +0 -88
- package/tests/exporttools.test.ts +0 -185
- package/tests/firefoxprep.test.ts +0 -134
- package/tests/flowlibrary.test.ts +0 -242
- package/tests/flowrun.test.ts +0 -303
- package/tests/forensics.test.ts +0 -439
- package/tests/gateway.test.ts +0 -939
- package/tests/headlesslib.test.ts +0 -171
- package/tests/httpstream.test.ts +0 -277
- package/tests/immutablelog.test.ts +0 -157
- package/tests/inboundguard.test.ts +0 -177
- package/tests/lazyload.test.ts +0 -72
- package/tests/librarymodes.test.ts +0 -623
- package/tests/llm.test.ts +0 -696
- package/tests/manifest.test.ts +0 -226
- package/tests/maskinputs.test.ts +0 -150
- package/tests/matrixverify.test.ts +0 -111
- package/tests/mavenpack.test.ts +0 -75
- package/tests/mcpserver.test.ts +0 -704
- package/tests/memory.test.ts +0 -4155
- package/tests/memorycare.test.ts +0 -230
- package/tests/migrateplan.test.ts +0 -349
- package/tests/minimization.test.ts +0 -414
- package/tests/modelroute.test.ts +0 -112
- package/tests/multiagent.test.ts +0 -440
- package/tests/nativebridge.test.ts +0 -807
- package/tests/navigation.test.ts +0 -284
- package/tests/nugetpack.test.ts +0 -83
- package/tests/orchestration.test.ts +0 -449
- package/tests/originpolicy.test.ts +0 -405
- package/tests/outputcompare.test.ts +0 -97
- package/tests/package.test.ts +0 -91
- package/tests/pagebridge.test.ts +0 -5909
- package/tests/pentest.test.ts +0 -75
- package/tests/perfrecords.test.ts +0 -257
- package/tests/permdiff.test.ts +0 -85
- package/tests/phishguard.test.ts +0 -128
- package/tests/pickerviews.test.ts +0 -155
- package/tests/pipelines.test.ts +0 -381
- package/tests/planlint.test.ts +0 -240
- package/tests/planreview.test.ts +0 -383
- package/tests/platformtargets.test.ts +0 -56
- package/tests/policy.test.ts +0 -8284
- package/tests/portability.test.ts +0 -96
- package/tests/progress.test.ts +0 -1036
- package/tests/promptlibrary.test.ts +0 -116
- package/tests/protocol.test.ts +0 -4793
- package/tests/publishingpipeline.test.ts +0 -314
- package/tests/quickactions.test.ts +0 -164
- package/tests/readiness.test.ts +0 -78
- package/tests/recipes.test.ts +0 -163
- package/tests/redactshots.test.ts +0 -216
- package/tests/registernvidiakeys.ts +0 -51
- package/tests/relayserve.test.ts +0 -304
- package/tests/releasenotes.test.ts +0 -276
- package/tests/resilience.test.ts +0 -467
- package/tests/resourceaware.test.ts +0 -187
- package/tests/runbudget.test.ts +0 -125
- package/tests/runhistory.test.ts +0 -299
- package/tests/runreplay.test.ts +0 -113
- package/tests/runresume.test.ts +0 -184
- package/tests/runstate.test.ts +0 -288
- package/tests/runtimeadapters.test.ts +0 -251
- package/tests/safariskeleton.test.ts +0 -115
- package/tests/sandboxframe.test.ts +0 -196
- package/tests/sbom.test.ts +0 -105
- package/tests/secretvault.test.ts +0 -188
- package/tests/selcache.test.ts +0 -111
- package/tests/sessioninterface.test.ts +0 -791
- package/tests/sessions.test.ts +0 -591
- package/tests/siteprefs.test.ts +0 -228
- package/tests/snapshotdelta.test.ts +0 -165
- package/tests/statusviews.test.ts +0 -237
- package/tests/stepmeter.test.ts +0 -200
- package/tests/streamparse.test.ts +0 -163
- package/tests/stylepruning.test.ts +0 -90
- package/tests/surfaces.test.ts +0 -282
- package/tests/sweep.test.ts +0 -78
- package/tests/syncbridge.test.ts +0 -164
- package/tests/taskqueue.test.ts +0 -206
- package/tests/toolcalls.test.ts +0 -336
- package/tests/toolcatalog.test.ts +0 -201
- package/tests/torture-agent.test.ts +0 -706
- package/tests/torture-auth.test.ts +0 -461
- package/tests/torture-commands.test.ts +0 -895
- package/tests/torture-data.test.ts +0 -923
- package/tests/torture-environments.test.ts +0 -285
- package/tests/torture-export.test.ts +0 -263
- package/tests/torture-http.test.ts +0 -1379
- package/tests/torture-llm.test.ts +0 -808
- package/tests/torture-memory.test.ts +0 -1084
- package/tests/torture-plan.test.ts +0 -829
- package/tests/torture-run.test.ts +0 -626
- package/tests/torture-session.test.ts +0 -928
- package/tests/torture-swarm.test.ts +0 -1245
- package/tests/torture-tools.test.ts +0 -964
- package/tests/torture-workflow.test.ts +0 -2354
- package/tests/tourviews.test.ts +0 -52
- package/tests/transparency.test.ts +0 -147
- package/tests/trigger.test.ts +0 -941
- package/tests/v1sunset.test.ts +0 -428
- package/tests/virtlist.test.ts +0 -94
- package/tests/vision.test.ts +0 -540
- package/tests/vsixpack.test.ts +0 -145
- package/tests/webapi.test.ts +0 -523
- package/tests/workflow.test.ts +0 -1785
- package/tests/workfloweditor.test.ts +0 -759
- package/tests/xpipack.test.ts +0 -128
- package/tools.ts +0 -734
- package/types.ts +0 -9908
- package/umd.ts +0 -9
- package/utils.ts +0 -249
- package/version.ts +0 -8
- package/versionregistry.ts +0 -1310
- package/views.ts +0 -2153
- package/vitest.config.ts +0 -12
- package/web/App.tsx +0 -78
- package/web/README.md +0 -47
- package/web/capacitor.config.ts +0 -46
- package/web/const.ts +0 -19
- package/web/control.shell.tsx +0 -66
- package/web/dashboardpage.ts +0 -554
- package/web/db.ts +0 -493
- package/web/design.html +0 -541
- package/web/gatewayview/Gateway.tsx +0 -345
- package/web/gatewayview/config.ts +0 -1042
- package/web/gatewayview/definition.ts +0 -174
- package/web/icons.ts +0 -15
- package/web/optionspage.ts +0 -691
- package/web/popup.ts +0 -1651
- package/web/schema.prisma +0 -176
- package/web/sidepanel.ts +0 -7941
- package/web/sitemanifest.json +0 -13
- package/web/terminal-ui.tsx +0 -622
- package/web/transparencypage.ts +0 -541
- package/web/vite.config.ts +0 -39
- package/web/workspace.ts +0 -23
- package/workbenchmemory.ts +0 -104
- package/workbenchsession.ts +0 -304
- package/workflow.ts +0 -4495
- /package/{dist/bun.d.ts → bun.d.ts} +0 -0
- /package/{dist/cli.d.ts → cli.d.ts} +0 -0
- /package/{dist/companion.d.ts → companion.d.ts} +0 -0
- /package/{dist/crossbrowser.d.ts → crossbrowser.d.ts} +0 -0
- /package/{dist/dashdone.d.ts → dashdone.d.ts} +0 -0
- /package/{dist/deno.d.ts → deno.d.ts} +0 -0
- /package/{dist/hardening.d.ts → hardening.d.ts} +0 -0
- /package/{dist/headless.d.ts → headless.d.ts} +0 -0
- /package/{dist/http.d.ts → http.d.ts} +0 -0
- /package/{dist/index.d.ts → index.d.ts} +0 -0
- /package/{dist/mcp.d.ts → mcp.d.ts} +0 -0
- /package/{dist/memory.d.ts → memory.d.ts} +0 -0
- /package/{dist/node.d.ts → node.d.ts} +0 -0
- /package/{dist/pack.d.ts → pack.d.ts} +0 -0
- /package/{dist/policy.d.ts → policy.d.ts} +0 -0
- /package/{dist/progress.d.ts → progress.d.ts} +0 -0
- /package/{dist/protocol.d.ts → protocol.d.ts} +0 -0
- /package/{dist/server.d.ts → server.d.ts} +0 -0
- /package/{dist/umd.d.ts → umd.d.ts} +0 -0
|
@@ -1,791 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { sessionmemory } from "../memory.js";
|
|
3
|
-
import { runsummarytask, summaryrequestof } from "../environments.js";
|
|
4
|
-
import {
|
|
5
|
-
addhistoryentry,
|
|
6
|
-
addrecallentry,
|
|
7
|
-
cancelrunactionof,
|
|
8
|
-
classifyfailure,
|
|
9
|
-
consentadvisoryverdict,
|
|
10
|
-
consentmemoryof,
|
|
11
|
-
distillrunsummary,
|
|
12
|
-
editedcorrectionof,
|
|
13
|
-
errorsurfaceof,
|
|
14
|
-
expirnotes,
|
|
15
|
-
expirerecallindex,
|
|
16
|
-
expirecorrections,
|
|
17
|
-
historyqueryof,
|
|
18
|
-
historysearch,
|
|
19
|
-
highlightterms,
|
|
20
|
-
matchingcorrections,
|
|
21
|
-
notebodyof,
|
|
22
|
-
notehistoryentry,
|
|
23
|
-
prunescratchpad,
|
|
24
|
-
rankrecall,
|
|
25
|
-
recallentryof,
|
|
26
|
-
rejectedcorrectionof,
|
|
27
|
-
retryhintof,
|
|
28
|
-
rollbackof,
|
|
29
|
-
rollbacksplit,
|
|
30
|
-
scratchentryof,
|
|
31
|
-
scratchpadof,
|
|
32
|
-
sessiongridrows,
|
|
33
|
-
sessionbundleof,
|
|
34
|
-
sitenoteof,
|
|
35
|
-
summaryhistoryentry,
|
|
36
|
-
tabsessionrefof,
|
|
37
|
-
tabsessionkey,
|
|
38
|
-
emptystatemessage,
|
|
39
|
-
sealnotebody,
|
|
40
|
-
opennotebody,
|
|
41
|
-
} from "../session.js";
|
|
42
|
-
import {
|
|
43
|
-
cancelrungate,
|
|
44
|
-
consentmemoryadvisorygate,
|
|
45
|
-
memoryreadscopegate,
|
|
46
|
-
retrydispatchgate,
|
|
47
|
-
scratchpadscopegate,
|
|
48
|
-
semanticrecallscopegate,
|
|
49
|
-
sessionretentionvalid,
|
|
50
|
-
sitenotesreadgate,
|
|
51
|
-
sitenoteswritegate,
|
|
52
|
-
summarywindowvalid,
|
|
53
|
-
} from "../policy.js";
|
|
54
|
-
import type { agentplan, scratchpadentry, storedrunlog } from "../types.js";
|
|
55
|
-
|
|
56
|
-
const now = 1_800_000_000_000;
|
|
57
|
-
|
|
58
|
-
class fakeadapter {
|
|
59
|
-
private readonly data = new Map<string, unknown>();
|
|
60
|
-
async get<T>(key: string): Promise<T | undefined> {
|
|
61
|
-
return this.data.get(key) as T | undefined;
|
|
62
|
-
}
|
|
63
|
-
async set<T>(key: string, value: T): Promise<void> {
|
|
64
|
-
this.data.set(key, value);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const plan: agentplan = {
|
|
69
|
-
id: "run1",
|
|
70
|
-
objective: "Collect the pricing table",
|
|
71
|
-
origin: "https://example.com",
|
|
72
|
-
steps: [
|
|
73
|
-
{ id: "s1", kind: "readtable", risk: "read", summary: "Read the pricing table." },
|
|
74
|
-
{ id: "s2", kind: "click", risk: "interaction", target: "#next", summary: "Open the next page." },
|
|
75
|
-
{ id: "s3", kind: "readtable", risk: "read", summary: "Read the second table." },
|
|
76
|
-
],
|
|
77
|
-
createdat: now - 5000,
|
|
78
|
-
expiresat: now + 5000,
|
|
79
|
-
state: "approved",
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
describe("session interface", () => {
|
|
83
|
-
it("builds site notes per origin with title, body, author provenance and timestamps", () => {
|
|
84
|
-
const note = sitenoteof({
|
|
85
|
-
origin: "https://example.com",
|
|
86
|
-
title: "Pricing flow",
|
|
87
|
-
body: "The pricing table loads under #pricing.",
|
|
88
|
-
author: "user",
|
|
89
|
-
now,
|
|
90
|
-
});
|
|
91
|
-
expect(note.id).toBeTruthy();
|
|
92
|
-
expect(note.body).toBe("The pricing table loads under #pricing.");
|
|
93
|
-
expect(note.sensitive).toBe(false);
|
|
94
|
-
expect(note.createdat).toBe(now);
|
|
95
|
-
expect(() => sitenoteof({ origin: " ", title: "t", body: "b", author: "user", now })).toThrow(/origin/);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("seals sensitive note bodies at rest and opens them for the reading surface", () => {
|
|
99
|
-
const note = sitenoteof({
|
|
100
|
-
origin: "https://bank.example",
|
|
101
|
-
title: "Login flow",
|
|
102
|
-
body: "The login asks the card number first.",
|
|
103
|
-
author: "user",
|
|
104
|
-
sensitive: true,
|
|
105
|
-
now,
|
|
106
|
-
});
|
|
107
|
-
expect(note.body).toBeUndefined();
|
|
108
|
-
expect(note.sealedbody).toMatch(/^sealed:/);
|
|
109
|
-
expect(note.sealedbody).not.toContain("card");
|
|
110
|
-
expect(notebodyof(note)).toBe("The login asks the card number first.");
|
|
111
|
-
expect(opennotebody(note.id, "sealed:notbase64")).toBe("");
|
|
112
|
-
expect(sealnotebody("id", "text")).not.toBe("text");
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it("expires site notes only at the user configured window", () => {
|
|
116
|
-
const notes = [
|
|
117
|
-
sitenoteof({ origin: "https://a.example", title: "fresh", body: "fresh note", author: "user", now }),
|
|
118
|
-
sitenoteof({ origin: "https://b.example", title: "old", body: "old note", author: "user", now: now - 10_000 }),
|
|
119
|
-
];
|
|
120
|
-
expect(expirnotes(notes, 5000, now)).toHaveLength(1);
|
|
121
|
-
expect(expirnotes(notes, undefined, now)).toHaveLength(2);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it("keeps the scratchpad append only per task with step provenance and prunes at the user window", () => {
|
|
125
|
-
const entry = scratchentryof({
|
|
126
|
-
taskid: "run1",
|
|
127
|
-
sessionid: "sess1",
|
|
128
|
-
text: "The table needs a wait first.",
|
|
129
|
-
stepid: "s1",
|
|
130
|
-
author: "agent",
|
|
131
|
-
now,
|
|
132
|
-
});
|
|
133
|
-
expect(entry.stepid).toBe("s1");
|
|
134
|
-
const usertext = scratchentryof({
|
|
135
|
-
taskid: "run1",
|
|
136
|
-
sessionid: "sess1",
|
|
137
|
-
text: "Remember the vat id.",
|
|
138
|
-
author: "user",
|
|
139
|
-
now: now - 10_000,
|
|
140
|
-
});
|
|
141
|
-
const other: scratchpadentry = { ...usertext, taskid: "run2" };
|
|
142
|
-
expect(scratchpadof([entry, usertext, other], "run1", "sess1")).toHaveLength(2);
|
|
143
|
-
expect(prunescratchpad([entry, usertext], 5000, now)).toEqual([entry]);
|
|
144
|
-
expect(prunescratchpad([entry, usertext], undefined, now)).toHaveLength(2);
|
|
145
|
-
expect(() => scratchentryof({ taskid: "run1", sessionid: "sess1", text: " ", author: "agent", now })).toThrow(
|
|
146
|
-
/text/,
|
|
147
|
-
);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("distills a run summary from step outcomes inside the user configured window with no fixed cap", () => {
|
|
151
|
-
const outcomes = [
|
|
152
|
-
{ stepid: "s1", ok: true, summary: "Read 12 rows.", at: now },
|
|
153
|
-
{ stepid: "s2", ok: true, summary: "Opened the next page.", at: now + 1 },
|
|
154
|
-
{ stepid: "s3", ok: false, summary: "The table never loaded.", at: now + 2 },
|
|
155
|
-
];
|
|
156
|
-
const full = distillrunsummary({
|
|
157
|
-
plan,
|
|
158
|
-
outcomes,
|
|
159
|
-
origins: ["https://example.com"],
|
|
160
|
-
sessionid: "sess1",
|
|
161
|
-
provenance: "offscreenworker",
|
|
162
|
-
now,
|
|
163
|
-
});
|
|
164
|
-
expect(full.steps).toHaveLength(3);
|
|
165
|
-
expect(full.origins).toEqual(["https://example.com"]);
|
|
166
|
-
expect(full.kinds).toEqual(["readtable", "click"]);
|
|
167
|
-
expect(full.task).toBe("runsummary");
|
|
168
|
-
const windowed = distillrunsummary({
|
|
169
|
-
plan,
|
|
170
|
-
outcomes,
|
|
171
|
-
origins: ["https://example.com"],
|
|
172
|
-
sessionid: "sess1",
|
|
173
|
-
window: 2,
|
|
174
|
-
provenance: "inline",
|
|
175
|
-
now,
|
|
176
|
-
});
|
|
177
|
-
expect(windowed.steps).toHaveLength(2);
|
|
178
|
-
expect(windowed.window).toBe(2);
|
|
179
|
-
expect(windowed.steps[0]?.stepid).toBe("s2");
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it("builds the runsummary request as one offscreen worker task", () => {
|
|
183
|
-
expect(runsummarytask).toBe("runsummary");
|
|
184
|
-
const request = summaryrequestof({
|
|
185
|
-
id: "w1",
|
|
186
|
-
runid: "run1",
|
|
187
|
-
sessionid: "sess1",
|
|
188
|
-
payload: JSON.stringify({ steps: 3 }),
|
|
189
|
-
sentat: now,
|
|
190
|
-
});
|
|
191
|
-
expect(request.task).toBe("runsummary");
|
|
192
|
-
expect(request.transferables).toEqual([]);
|
|
193
|
-
expect(() => summaryrequestof({ id: "w2", runid: "run1", sessionid: "sess1", payload: " ", sentat: now })).toThrow(
|
|
194
|
-
/payload/,
|
|
195
|
-
);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it("indexes extraction records with fingerprint deduplication and ranks them by text similarity with provenance", () => {
|
|
199
|
-
const first = recallentryof({
|
|
200
|
-
origin: "https://example.com",
|
|
201
|
-
runid: "run1",
|
|
202
|
-
stepid: "s1",
|
|
203
|
-
text: "pricing table with monthly and yearly plans",
|
|
204
|
-
at: now,
|
|
205
|
-
});
|
|
206
|
-
const duplicate = recallentryof({
|
|
207
|
-
origin: "https://example.com",
|
|
208
|
-
runid: "run2",
|
|
209
|
-
stepid: "s9",
|
|
210
|
-
text: "pricing table with monthly and yearly plans",
|
|
211
|
-
at: now + 1,
|
|
212
|
-
});
|
|
213
|
-
let index = addrecallentry([], first);
|
|
214
|
-
index = addrecallentry(index, duplicate);
|
|
215
|
-
expect(index).toHaveLength(1);
|
|
216
|
-
const other = recallentryof({
|
|
217
|
-
origin: "https://shop.example",
|
|
218
|
-
runid: "run1",
|
|
219
|
-
stepid: "s2",
|
|
220
|
-
text: "shipping options and delivery windows",
|
|
221
|
-
at: now,
|
|
222
|
-
});
|
|
223
|
-
index = addrecallentry(index, other);
|
|
224
|
-
const ranked = rankrecall(
|
|
225
|
-
index,
|
|
226
|
-
{ text: "pricing plans" },
|
|
227
|
-
{ origins: ["https://example.com", "https://shop.example"] },
|
|
228
|
-
);
|
|
229
|
-
expect(ranked[0]?.entry.origin).toBe("https://example.com");
|
|
230
|
-
expect(ranked[0]?.entry.runid).toBe("run1");
|
|
231
|
-
expect(ranked[0]?.entry.stepid).toBe("s1");
|
|
232
|
-
expect(ranked[0]?.reason).toMatch(/run run1/);
|
|
233
|
-
expect(rankrecall(index, { text: "nothing alike" }, { origins: ["https://example.com"] })).toHaveLength(0);
|
|
234
|
-
expect(expirerecallindex(index, 5000, now + 10_000)).toHaveLength(0);
|
|
235
|
-
expect(expirerecallindex(index, undefined, now)).toHaveLength(2);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
it("captures corrections from plan review edits and rejections and feeds matching ones into proposals", () => {
|
|
239
|
-
const edited = editedcorrectionof({
|
|
240
|
-
origin: "https://example.com",
|
|
241
|
-
kind: "click",
|
|
242
|
-
stepid: "s2",
|
|
243
|
-
original: "click #next",
|
|
244
|
-
corrected: "click #next-page",
|
|
245
|
-
reason: "The target changed.",
|
|
246
|
-
now: now - 10_000,
|
|
247
|
-
});
|
|
248
|
-
expect(edited.source).toBe("edited");
|
|
249
|
-
expect(() =>
|
|
250
|
-
editedcorrectionof({
|
|
251
|
-
origin: "https://example.com",
|
|
252
|
-
kind: "click",
|
|
253
|
-
stepid: "s2",
|
|
254
|
-
original: "same",
|
|
255
|
-
corrected: "same",
|
|
256
|
-
reason: "r",
|
|
257
|
-
now,
|
|
258
|
-
}),
|
|
259
|
-
).toThrow(/changed/);
|
|
260
|
-
const rejected = rejectedcorrectionof({
|
|
261
|
-
origin: "https://example.com",
|
|
262
|
-
kind: "readtable",
|
|
263
|
-
stepid: "s3",
|
|
264
|
-
original: "readtable #t2",
|
|
265
|
-
reason: "The user refused the second table.",
|
|
266
|
-
now: now - 1000,
|
|
267
|
-
});
|
|
268
|
-
expect(rejected.corrected).toBeUndefined();
|
|
269
|
-
const matches = matchingcorrections([edited, rejected], { origin: "https://example.com", kind: "readtable" });
|
|
270
|
-
expect(matches).toEqual([rejected]);
|
|
271
|
-
expect(expirecorrections([edited, rejected], 5000, now)).toEqual([rejected]);
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it("records consent memory decisions per origin with expiry and boundary fields and keeps them advisory", () => {
|
|
275
|
-
const grant = consentmemoryof({
|
|
276
|
-
origin: "https://example.com",
|
|
277
|
-
decision: "grant",
|
|
278
|
-
boundary: "one hour the user chose",
|
|
279
|
-
kinds: ["click"],
|
|
280
|
-
expiresat: now + 3600_000,
|
|
281
|
-
now,
|
|
282
|
-
});
|
|
283
|
-
const denial = consentmemoryof({
|
|
284
|
-
origin: "https://example.com",
|
|
285
|
-
decision: "deny",
|
|
286
|
-
boundary: "the origin profile the user edits",
|
|
287
|
-
kinds: ["fillcard"],
|
|
288
|
-
now,
|
|
289
|
-
});
|
|
290
|
-
expect(grant.expiresat).toBe(now + 3600_000);
|
|
291
|
-
expect(consentadvisoryverdict([grant, denial], "https://example.com", "fillcard").reason).toMatch(/prior denial/);
|
|
292
|
-
expect(consentadvisoryverdict([grant], "https://example.com", "click").reason).toMatch(/never auto grants/);
|
|
293
|
-
expect(consentadvisoryverdict([], "https://example.com", "click").advisory).toBe(false);
|
|
294
|
-
expect(consentadvisoryverdict([grant], "https://example.com", "click").advisory).toBe(true);
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
it("derives session grid rows from the session stores with no new state", () => {
|
|
298
|
-
const log: storedrunlog = {
|
|
299
|
-
runid: "run0",
|
|
300
|
-
sessionid: "sess0",
|
|
301
|
-
entries: [
|
|
302
|
-
{
|
|
303
|
-
id: "e1",
|
|
304
|
-
runid: "run0",
|
|
305
|
-
kind: "step",
|
|
306
|
-
summary: "The readtable step completed.",
|
|
307
|
-
origin: "https://old.example",
|
|
308
|
-
stepid: "s0",
|
|
309
|
-
at: now - 1000,
|
|
310
|
-
hash: { previous: "genesis", current: "h1", algorithm: "sha-256" },
|
|
311
|
-
},
|
|
312
|
-
],
|
|
313
|
-
seal: {
|
|
314
|
-
runid: "run0",
|
|
315
|
-
entries: 1,
|
|
316
|
-
sealhash: { previous: "h1", current: "seal", algorithm: "sha-256" },
|
|
317
|
-
sealedat: now - 500,
|
|
318
|
-
},
|
|
319
|
-
updatedat: now - 500,
|
|
320
|
-
};
|
|
321
|
-
const rows = sessiongridrows({
|
|
322
|
-
session: {
|
|
323
|
-
id: "sess1",
|
|
324
|
-
tabid: 7,
|
|
325
|
-
origin: "https://example.com",
|
|
326
|
-
pausedat: now - 10,
|
|
327
|
-
grants: ["https://example.com"],
|
|
328
|
-
},
|
|
329
|
-
plan,
|
|
330
|
-
progress: { planid: "run1", completedsteps: ["s1"], updatedat: now },
|
|
331
|
-
logs: [log],
|
|
332
|
-
summaries: [
|
|
333
|
-
{
|
|
334
|
-
runid: "run0",
|
|
335
|
-
sessionid: "sess0",
|
|
336
|
-
origins: ["https://old.example"],
|
|
337
|
-
kinds: ["readtable"],
|
|
338
|
-
steps: [{ stepid: "s0", kind: "readtable", ok: true, summary: "done" }],
|
|
339
|
-
task: "runsummary",
|
|
340
|
-
provenance: "inline",
|
|
341
|
-
distilledat: now - 400,
|
|
342
|
-
},
|
|
343
|
-
],
|
|
344
|
-
locks: [{ runid: "run1" }],
|
|
345
|
-
tabsessions: [
|
|
346
|
-
{ tabid: 3, sessionid: "sess0", runid: "run0", origin: "https://old.example", updatedat: now - 1000 },
|
|
347
|
-
],
|
|
348
|
-
});
|
|
349
|
-
expect(rows).toHaveLength(2);
|
|
350
|
-
const live = rows.find((row) => row.state === "live");
|
|
351
|
-
const saved = rows.find((row) => row.state === "saved");
|
|
352
|
-
expect(live?.runid).toBe("run1");
|
|
353
|
-
expect(live?.steps).toBe(3);
|
|
354
|
-
expect(live?.completed).toBe(1);
|
|
355
|
-
expect(live?.lock).toBe("held");
|
|
356
|
-
expect(live?.actions).toEqual(["cancelrun", "resume"]);
|
|
357
|
-
expect(saved?.runid).toBe("run0");
|
|
358
|
-
expect(saved?.outcome).toBe("completed");
|
|
359
|
-
expect(saved?.sealhash).toBe("seal");
|
|
360
|
-
expect(saved?.actions).toEqual(["reopen"]);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it("searches sessions, notes and summaries from one box with filters and matched term highlighting", () => {
|
|
364
|
-
let corpus = addhistoryentry([], {
|
|
365
|
-
source: "session",
|
|
366
|
-
id: "sess1",
|
|
367
|
-
origin: "https://example.com",
|
|
368
|
-
title: "Session of https://example.com",
|
|
369
|
-
text: "session started for the pricing work",
|
|
370
|
-
at: now - 3000,
|
|
371
|
-
});
|
|
372
|
-
corpus = addhistoryentry(
|
|
373
|
-
corpus,
|
|
374
|
-
notehistoryentry(
|
|
375
|
-
sitenoteof({
|
|
376
|
-
origin: "https://example.com",
|
|
377
|
-
title: "Pricing notes",
|
|
378
|
-
body: "The yearly plan needs a confirmation dialog.",
|
|
379
|
-
author: "user",
|
|
380
|
-
now: now - 2000,
|
|
381
|
-
id: "note1",
|
|
382
|
-
}),
|
|
383
|
-
),
|
|
384
|
-
);
|
|
385
|
-
corpus = addhistoryentry(
|
|
386
|
-
corpus,
|
|
387
|
-
summaryhistoryentry(
|
|
388
|
-
distillrunsummary({
|
|
389
|
-
plan,
|
|
390
|
-
outcomes: [{ stepid: "s1", ok: true, summary: "Read the pricing rows.", at: now }],
|
|
391
|
-
origins: ["https://example.com"],
|
|
392
|
-
sessionid: "sess1",
|
|
393
|
-
provenance: "inline",
|
|
394
|
-
now: now - 1000,
|
|
395
|
-
}),
|
|
396
|
-
),
|
|
397
|
-
);
|
|
398
|
-
const query = historyqueryof({ text: "pricing yearly" });
|
|
399
|
-
expect(query).toBeDefined();
|
|
400
|
-
const hits = historysearch(corpus, query as { text: string });
|
|
401
|
-
expect(hits).toHaveLength(3);
|
|
402
|
-
expect(hits.every((hit) => hit.highlights.length > 0)).toBe(true);
|
|
403
|
-
expect(highlightterms("The pricing table shows yearly plans", "pricing yearly")).toEqual(["pricing", "yearly"]);
|
|
404
|
-
expect(historysearch(corpus, { text: "pricing", origin: "https://other.example" })).toHaveLength(0);
|
|
405
|
-
expect(historysearch(corpus, { text: "pricing", from: now - 1500 })).toHaveLength(1);
|
|
406
|
-
expect(historysearch(corpus, { text: "pricing", outcome: "completed" }).map((hit) => hit.source)).toEqual([
|
|
407
|
-
"summary",
|
|
408
|
-
]);
|
|
409
|
-
expect(historyqueryof({ text: "pricing", from: now, to: now - 1 })).toBeUndefined();
|
|
410
|
-
expect(historyqueryof({ text: " " })).toBeUndefined();
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
it("classifies error causes as page, network, policy or gate and serves retry hints with the policy verdict", () => {
|
|
414
|
-
expect(classifyfailure({ message: "The element never appeared.", policyrefused: false, gatewait: false })).toBe(
|
|
415
|
-
"page",
|
|
416
|
-
);
|
|
417
|
-
expect(
|
|
418
|
-
classifyfailure({ message: "The fetch failed with a network error.", policyrefused: false, gatewait: false }),
|
|
419
|
-
).toBe("network");
|
|
420
|
-
expect(
|
|
421
|
-
classifyfailure({ message: "The origin never granted the step.", policyrefused: true, gatewait: false }),
|
|
422
|
-
).toBe("policy");
|
|
423
|
-
expect(
|
|
424
|
-
classifyfailure({ message: "The payment waits for the confirm gate.", policyrefused: false, gatewait: true }),
|
|
425
|
-
).toBe("gate");
|
|
426
|
-
const surface = errorsurfaceof({
|
|
427
|
-
stepid: "s3",
|
|
428
|
-
runid: "run1",
|
|
429
|
-
message: "The table never loaded.",
|
|
430
|
-
cause: "page",
|
|
431
|
-
retryallowed: true,
|
|
432
|
-
retryreason: "A new reviewed dispatch may retry the read.",
|
|
433
|
-
context: { origin: "https://example.com", kind: "readtable" },
|
|
434
|
-
now,
|
|
435
|
-
});
|
|
436
|
-
expect(retryhintof(surface).reason).toMatch(/new reviewed dispatch/);
|
|
437
|
-
expect(
|
|
438
|
-
retryhintof({ ...surface, retry: { allowed: false, reason: "The step exhausted its retries." } }).allowed,
|
|
439
|
-
).toBe(false);
|
|
440
|
-
expect(() =>
|
|
441
|
-
errorsurfaceof({
|
|
442
|
-
stepid: "s3",
|
|
443
|
-
runid: "run1",
|
|
444
|
-
message: " ",
|
|
445
|
-
cause: "page",
|
|
446
|
-
retryallowed: true,
|
|
447
|
-
retryreason: "r",
|
|
448
|
-
context: {},
|
|
449
|
-
now,
|
|
450
|
-
}),
|
|
451
|
-
).toThrow(/message/);
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
it("cancels a run with a rollback of the queued steps only while executed steps stay untouched", () => {
|
|
455
|
-
const progress = { planid: "run1", completedsteps: ["s1", "s2"], updatedat: now };
|
|
456
|
-
const split = rollbacksplit(plan, progress);
|
|
457
|
-
expect(split.executedstepids).toEqual(["s1", "s2"]);
|
|
458
|
-
expect(split.queuedstepids).toEqual(["s3"]);
|
|
459
|
-
const action = cancelrunactionof({ runid: "run1", sessionid: "sess1", plan, progress, preference: "queued" });
|
|
460
|
-
expect(action.rollback.scope).toBe("queued");
|
|
461
|
-
expect(action.rollback.queuedstepids).toEqual(["s3"]);
|
|
462
|
-
expect(action.rollback.label).toMatch(/sealed log/);
|
|
463
|
-
const none = rollbackof(plan, progress, "none");
|
|
464
|
-
expect(none.scope).toBe("none");
|
|
465
|
-
expect(none.label).toMatch(/without a rollback/);
|
|
466
|
-
const gate = cancelrungate({
|
|
467
|
-
queuedstepids: action.rollback.queuedstepids,
|
|
468
|
-
executedstepids: split.executedstepids,
|
|
469
|
-
rollbackscope: "queued",
|
|
470
|
-
});
|
|
471
|
-
expect(gate.allowed).toBe(true);
|
|
472
|
-
expect(gate.reason).toMatch(/stay untouched in the sealed log/);
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
it("isolates session state per tab so parallel tabs never collide", () => {
|
|
476
|
-
expect(tabsessionkey(12)).toBe("tabsession:12");
|
|
477
|
-
const ref = tabsessionrefof({ tabid: 12, sessionid: "sessA", runid: "runA", origin: "https://a.example", now });
|
|
478
|
-
expect(ref.runid).toBe("runA");
|
|
479
|
-
expect(() => tabsessionrefof({ tabid: -1, sessionid: "sessA", origin: "https://a.example", now })).toThrow(/tab/);
|
|
480
|
-
const other = tabsessionrefof({ tabid: 13, sessionid: "sessB", origin: "https://b.example", now });
|
|
481
|
-
expect(ref.sessionid).not.toBe(other.sessionid);
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
it("serves the empty state guidance of the four interface surfaces", () => {
|
|
485
|
-
expect(emptystatemessage("sessiongrid")).toMatch(/first run/);
|
|
486
|
-
expect(emptystatemessage("historysearch")).toMatch(/first query/);
|
|
487
|
-
expect(emptystatemessage("sitenotes", "https://example.com")).toMatch(/for https:\/\/example.com/);
|
|
488
|
-
expect(emptystatemessage("scratchpad")).toMatch(/step provenance/);
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
it("exports notes, summaries and corrections as one audit bundle with sealed sensitive bodies", () => {
|
|
492
|
-
const note = sitenoteof({
|
|
493
|
-
origin: "https://bank.example",
|
|
494
|
-
title: "Login",
|
|
495
|
-
body: "card number first",
|
|
496
|
-
author: "user",
|
|
497
|
-
sensitive: true,
|
|
498
|
-
now,
|
|
499
|
-
});
|
|
500
|
-
const bundle = sessionbundleof({ notes: [note], summaries: [], corrections: [], exportedat: now });
|
|
501
|
-
expect(bundle.kind).toBe("sessionbundle");
|
|
502
|
-
expect(bundle.notes[0]?.sealedbody).toMatch(/^sealed:/);
|
|
503
|
-
expect(bundle.notes[0]?.body).toBeUndefined();
|
|
504
|
-
});
|
|
505
|
-
});
|
|
506
|
-
|
|
507
|
-
describe("session interface gates", () => {
|
|
508
|
-
it("gates sitenotes reads to granted origins and writes behind explicit consent", () => {
|
|
509
|
-
expect(sitenotesreadgate({ origin: "https://example.com", grants: ["https://example.com"] }).allowed).toBe(true);
|
|
510
|
-
expect(sitenotesreadgate({ origin: "https://other.example", grants: ["https://example.com"] }).reason).toMatch(
|
|
511
|
-
/never granted/,
|
|
512
|
-
);
|
|
513
|
-
expect(sitenoteswritegate({ consent: false, origin: "https://example.com" }).allowed).toBe(false);
|
|
514
|
-
expect(sitenoteswritegate({ consent: true, origin: "https://example.com" }).reason).toMatch(/author provenance/);
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
it("scopes scratchpad access to the owning task session and memory reads to planning and prompting", () => {
|
|
518
|
-
expect(
|
|
519
|
-
scratchpadscopegate({ taskid: "run1", sessionid: "sess1", entrytaskid: "run1", entrysessionid: "sess1" }).allowed,
|
|
520
|
-
).toBe(true);
|
|
521
|
-
expect(
|
|
522
|
-
scratchpadscopegate({ taskid: "run2", sessionid: "sess1", entrytaskid: "run1", entrysessionid: "sess1" }).reason,
|
|
523
|
-
).toMatch(/never crosses/);
|
|
524
|
-
expect(memoryreadscopegate({ phase: "planning" }).allowed).toBe(true);
|
|
525
|
-
expect(memoryreadscopegate({ phase: "prompting" }).allowed).toBe(true);
|
|
526
|
-
expect(memoryreadscopegate({ phase: "execution" }).allowed).toBe(false);
|
|
527
|
-
expect(memoryreadscopegate({ phase: "idle" }).reason).toMatch(/planning and the prompting alone/);
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
it("refuses semantic recall across origins outside the run scope", () => {
|
|
531
|
-
expect(semanticrecallscopegate({ origin: undefined, scope: ["https://example.com"] }).allowed).toBe(true);
|
|
532
|
-
expect(semanticrecallscopegate({ origin: "https://example.com", scope: ["https://example.com"] }).allowed).toBe(
|
|
533
|
-
true,
|
|
534
|
-
);
|
|
535
|
-
expect(semanticrecallscopegate({ origin: "https://other.example", scope: ["https://example.com"] }).reason).toMatch(
|
|
536
|
-
/outside the run scope/,
|
|
537
|
-
);
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
it("validates the summary window and the retention windows as user choices with no engine cap", () => {
|
|
541
|
-
expect(summarywindowvalid(undefined).reason).toMatch(/no fixed cap/);
|
|
542
|
-
expect(summarywindowvalid(5).allowed).toBe(true);
|
|
543
|
-
expect(summarywindowvalid(-1).allowed).toBe(false);
|
|
544
|
-
expect(summarywindowvalid(1.5).allowed).toBe(false);
|
|
545
|
-
expect(sessionretentionvalid(undefined).allowed).toBe(true);
|
|
546
|
-
expect(sessionretentionvalid(1000).allowed).toBe(true);
|
|
547
|
-
expect(sessionretentionvalid(0).reason).toMatch(/positive user value/);
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
it("keeps consent memory advisory with denials carrying the same weight as grants", () => {
|
|
551
|
-
expect(
|
|
552
|
-
consentmemoryadvisorygate({ auto: true, latest: { decision: "grant", origin: "https://example.com" } }).allowed,
|
|
553
|
-
).toBe(false);
|
|
554
|
-
expect(
|
|
555
|
-
consentmemoryadvisorygate({ auto: false, latest: { decision: "deny", origin: "https://example.com" } }).reason,
|
|
556
|
-
).toMatch(/same weight/);
|
|
557
|
-
expect(consentmemoryadvisorygate({ auto: false }).reason).toMatch(/advisory/);
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
it("lets a retry pass only through a new reviewed dispatch", () => {
|
|
561
|
-
expect(retrydispatchgate({ reviewed: false, stepid: "s3" }).allowed).toBe(false);
|
|
562
|
-
expect(retrydispatchgate({ reviewed: true, stepid: "s3" }).reason).toMatch(/full consent gate chain/);
|
|
563
|
-
});
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
describe("session interface memory accessors", () => {
|
|
567
|
-
it("stores site notes with read, write, list and expiry per origin", async () => {
|
|
568
|
-
const store = new sessionmemory(new fakeadapter());
|
|
569
|
-
const note = sitenoteof({
|
|
570
|
-
origin: "https://example.com",
|
|
571
|
-
title: "Pricing",
|
|
572
|
-
body: "Table under #pricing.",
|
|
573
|
-
author: "user",
|
|
574
|
-
now,
|
|
575
|
-
});
|
|
576
|
-
await store.writesitenote(note);
|
|
577
|
-
await store.writesitenote(editnoteof(note, "Pricing", "Table under #pricing-table.", "user", now + 1));
|
|
578
|
-
expect((await store.readsitenotes("https://example.com"))[0]?.title).toBe("Pricing");
|
|
579
|
-
expect(await store.getsitenotes()).toHaveLength(1);
|
|
580
|
-
await store.writesitenote(
|
|
581
|
-
sitenoteof({ origin: "https://old.example", title: "Old", body: "old", author: "user", now: now - 10_000 }),
|
|
582
|
-
);
|
|
583
|
-
expect(await store.expiresitenotes(5000, now)).toHaveLength(1);
|
|
584
|
-
await store.removesitenote(note.id);
|
|
585
|
-
expect(await store.getsitenotes()).toHaveLength(0);
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
it("appends and reads the scratchpad per task and prunes at the user window", async () => {
|
|
589
|
-
const store = new sessionmemory(new fakeadapter());
|
|
590
|
-
await store.appendscratchentry(
|
|
591
|
-
scratchentryof({ taskid: "run1", sessionid: "sess1", text: "first", author: "agent", now }),
|
|
592
|
-
);
|
|
593
|
-
await store.appendscratchentry(
|
|
594
|
-
scratchentryof({ taskid: "run1", sessionid: "sess1", text: "second", author: "agent", now: now + 1 }),
|
|
595
|
-
);
|
|
596
|
-
await store.appendscratchentry(
|
|
597
|
-
scratchentryof({ taskid: "run2", sessionid: "sess1", text: "other task", author: "agent", now }),
|
|
598
|
-
);
|
|
599
|
-
expect(await store.readscratchpad("run1", "sess1")).toHaveLength(2);
|
|
600
|
-
expect(await store.prunescratchentries(5000, now + 10_000)).toHaveLength(0);
|
|
601
|
-
});
|
|
602
|
-
|
|
603
|
-
it("stores run summaries per run and lists them per origin", async () => {
|
|
604
|
-
const store = new sessionmemory(new fakeadapter());
|
|
605
|
-
const summary = distillrunsummary({
|
|
606
|
-
plan,
|
|
607
|
-
outcomes: [{ stepid: "s1", ok: true, summary: "Read 12 rows.", at: now }],
|
|
608
|
-
origins: ["https://example.com"],
|
|
609
|
-
sessionid: "sess1",
|
|
610
|
-
provenance: "offscreenworker",
|
|
611
|
-
now,
|
|
612
|
-
});
|
|
613
|
-
await store.setrunsummary(summary);
|
|
614
|
-
await store.trackrunsummary(summary.runid);
|
|
615
|
-
expect(await store.listrunsummaries("https://example.com")).toHaveLength(1);
|
|
616
|
-
expect((await store.getrunsummary("run1"))?.provenance).toBe("offscreenworker");
|
|
617
|
-
expect(await store.getrunsummary("missing")).toBeUndefined();
|
|
618
|
-
expect(await store.expirerunsummaries(5000, now + 10_000)).toHaveLength(0);
|
|
619
|
-
expect((await store.getrunsummary("run1"))?.steps).toEqual([]);
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
it("answers semantic recall queries across the extraction store with deduplication and expiry", async () => {
|
|
623
|
-
const store = new sessionmemory(new fakeadapter());
|
|
624
|
-
await store.addrecallentry(
|
|
625
|
-
recallentryof({
|
|
626
|
-
origin: "https://example.com",
|
|
627
|
-
runid: "run1",
|
|
628
|
-
stepid: "s1",
|
|
629
|
-
text: "pricing table plans",
|
|
630
|
-
at: now,
|
|
631
|
-
}),
|
|
632
|
-
);
|
|
633
|
-
await store.addrecallentry(
|
|
634
|
-
recallentryof({
|
|
635
|
-
origin: "https://example.com",
|
|
636
|
-
runid: "run1",
|
|
637
|
-
stepid: "s1",
|
|
638
|
-
text: "pricing table plans",
|
|
639
|
-
at: now + 1,
|
|
640
|
-
}),
|
|
641
|
-
);
|
|
642
|
-
expect(await store.getrecallindex()).toHaveLength(1);
|
|
643
|
-
const matches = await store.semanticrecall({ text: "pricing" }, { origins: ["https://example.com"] }, rankrecall);
|
|
644
|
-
expect(matches[0]?.entry.stepid).toBe("s1");
|
|
645
|
-
expect(await store.expirerecallentries(5000, now + 10_000)).toHaveLength(0);
|
|
646
|
-
});
|
|
647
|
-
|
|
648
|
-
it("records and queries correction memory and consent memory per origin", async () => {
|
|
649
|
-
const store = new sessionmemory(new fakeadapter());
|
|
650
|
-
await store.addcorrection(
|
|
651
|
-
rejectedcorrectionof({
|
|
652
|
-
origin: "https://example.com",
|
|
653
|
-
kind: "readtable",
|
|
654
|
-
stepid: "s3",
|
|
655
|
-
original: "readtable #t2",
|
|
656
|
-
reason: "Refused.",
|
|
657
|
-
now,
|
|
658
|
-
}),
|
|
659
|
-
);
|
|
660
|
-
await store.addcorrection(
|
|
661
|
-
editedcorrectionof({
|
|
662
|
-
origin: "https://shop.example",
|
|
663
|
-
kind: "click",
|
|
664
|
-
stepid: "s2",
|
|
665
|
-
original: "click #a",
|
|
666
|
-
corrected: "click #b",
|
|
667
|
-
reason: "Target moved.",
|
|
668
|
-
now,
|
|
669
|
-
}),
|
|
670
|
-
);
|
|
671
|
-
expect(await store.getcorrections({ origin: "https://example.com" })).toHaveLength(1);
|
|
672
|
-
expect(await store.getcorrections({ kind: "click" })).toHaveLength(1);
|
|
673
|
-
expect(await store.expirecorrectionentries(5000, now + 10_000)).toHaveLength(0);
|
|
674
|
-
await store.addconsentmemoryentry(
|
|
675
|
-
consentmemoryof({
|
|
676
|
-
origin: "https://example.com",
|
|
677
|
-
decision: "grant",
|
|
678
|
-
boundary: "one hour",
|
|
679
|
-
kinds: ["click"],
|
|
680
|
-
expiresat: now + 3600_000,
|
|
681
|
-
now,
|
|
682
|
-
}),
|
|
683
|
-
);
|
|
684
|
-
await store.addconsentmemoryentry(
|
|
685
|
-
consentmemoryof({
|
|
686
|
-
origin: "https://example.com",
|
|
687
|
-
decision: "deny",
|
|
688
|
-
boundary: "profile edit",
|
|
689
|
-
kinds: ["fillcard"],
|
|
690
|
-
now,
|
|
691
|
-
}),
|
|
692
|
-
);
|
|
693
|
-
expect(await store.getconsentmemory("https://example.com")).toHaveLength(2);
|
|
694
|
-
expect(await store.getconsentmemory()).toHaveLength(2);
|
|
695
|
-
});
|
|
696
|
-
|
|
697
|
-
it("records error surfaces per failed step with their retry hints", async () => {
|
|
698
|
-
const store = new sessionmemory(new fakeadapter());
|
|
699
|
-
const surface = errorsurfaceof({
|
|
700
|
-
stepid: "s3",
|
|
701
|
-
runid: "run1",
|
|
702
|
-
message: "The table never loaded.",
|
|
703
|
-
cause: "page",
|
|
704
|
-
retryallowed: true,
|
|
705
|
-
retryreason: "A new reviewed dispatch may retry.",
|
|
706
|
-
context: { kind: "readtable" },
|
|
707
|
-
now,
|
|
708
|
-
});
|
|
709
|
-
await store.adderrorsurface(surface);
|
|
710
|
-
expect(await store.geterrorsurfaces("s3")).toHaveLength(1);
|
|
711
|
-
expect((await store.geterrorsurfaces())[0]?.retry.allowed).toBe(true);
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
it("builds the history index incrementally and answers one search across the corpus", async () => {
|
|
715
|
-
const store = new sessionmemory(new fakeadapter());
|
|
716
|
-
await store.addhistoryentry({
|
|
717
|
-
source: "note",
|
|
718
|
-
id: "n1",
|
|
719
|
-
origin: "https://example.com",
|
|
720
|
-
title: "Pricing notes",
|
|
721
|
-
text: "yearly plan details",
|
|
722
|
-
at: now,
|
|
723
|
-
});
|
|
724
|
-
await store.addhistoryentry({
|
|
725
|
-
source: "note",
|
|
726
|
-
id: "n1",
|
|
727
|
-
origin: "https://example.com",
|
|
728
|
-
title: "Pricing notes edited",
|
|
729
|
-
text: "yearly plan details",
|
|
730
|
-
at: now + 1,
|
|
731
|
-
});
|
|
732
|
-
expect(await store.gethistoryindex()).toHaveLength(1);
|
|
733
|
-
const hits = await store.historysearch({ text: "yearly" }, historysearch);
|
|
734
|
-
expect(hits[0]?.title).toBe("Pricing notes edited");
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
it("isolates per tab session references and lists them for the session grid", async () => {
|
|
738
|
-
const store = new sessionmemory(new fakeadapter());
|
|
739
|
-
await store.settabsession(
|
|
740
|
-
tabsessionrefof({ tabid: 1, sessionid: "sessA", runid: "runA", origin: "https://a.example", now }),
|
|
741
|
-
);
|
|
742
|
-
await store.settabsession(tabsessionrefof({ tabid: 2, sessionid: "sessB", origin: "https://b.example", now }));
|
|
743
|
-
await store.tracktabsession(1);
|
|
744
|
-
await store.tracktabsession(2);
|
|
745
|
-
await store.tracktabsession(1);
|
|
746
|
-
const refs = await store.listtabsessions();
|
|
747
|
-
expect(refs).toHaveLength(2);
|
|
748
|
-
expect((await store.gettabsession(1))?.runid).toBe("runA");
|
|
749
|
-
expect(await store.gettabsession(9)).toBeUndefined();
|
|
750
|
-
});
|
|
751
|
-
|
|
752
|
-
it("exports notes, summaries and corrections as one audit bundle", async () => {
|
|
753
|
-
const store = new sessionmemory(new fakeadapter());
|
|
754
|
-
await store.writesitenote(
|
|
755
|
-
sitenoteof({
|
|
756
|
-
origin: "https://example.com",
|
|
757
|
-
title: "Pricing",
|
|
758
|
-
body: "Table under #pricing.",
|
|
759
|
-
author: "user",
|
|
760
|
-
now,
|
|
761
|
-
}),
|
|
762
|
-
);
|
|
763
|
-
await store.addcorrection(
|
|
764
|
-
rejectedcorrectionof({
|
|
765
|
-
origin: "https://example.com",
|
|
766
|
-
kind: "click",
|
|
767
|
-
stepid: "s1",
|
|
768
|
-
original: "click #a",
|
|
769
|
-
reason: "Refused.",
|
|
770
|
-
now,
|
|
771
|
-
}),
|
|
772
|
-
);
|
|
773
|
-
const bundle = await store.exportsessionbundle(now);
|
|
774
|
-
expect(bundle.kind).toBe("sessionbundle");
|
|
775
|
-
expect(bundle.notes).toHaveLength(1);
|
|
776
|
-
expect(bundle.corrections).toHaveLength(1);
|
|
777
|
-
});
|
|
778
|
-
});
|
|
779
|
-
|
|
780
|
-
/** Local helper mirroring the panel note edit for the accessor test. */
|
|
781
|
-
function editnoteof(
|
|
782
|
-
note: ReturnType<typeof sitenoteof>,
|
|
783
|
-
title: string,
|
|
784
|
-
body: string,
|
|
785
|
-
author: string,
|
|
786
|
-
at: number,
|
|
787
|
-
): ReturnType<typeof sitenoteof> {
|
|
788
|
-
return note.sensitive
|
|
789
|
-
? { ...note, title, sealedbody: sealnotebody(note.id, body), updatedat: at, author }
|
|
790
|
-
: { ...note, title, body, updatedat: at, author };
|
|
791
|
-
}
|