@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
package/tests/cspaudit.test.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
|
|
4
|
-
/* ── The 1.1.95 content security audit policy set. ── */
|
|
5
|
-
|
|
6
|
-
type manifestshape = {
|
|
7
|
-
content_security_policy?: { extension_pages?: string };
|
|
8
|
-
sandbox?: { pages?: string[] };
|
|
9
|
-
browsers?: Record<string, { content_security_policy?: { extension_pages?: string } }>;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
describe("the cspaudit policy set", () => {
|
|
13
|
-
it("matches the manifest policies of the root and both browser overlays", async () => {
|
|
14
|
-
const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as manifestshape;
|
|
15
|
-
const rootpolicy = manifest.content_security_policy?.extension_pages;
|
|
16
|
-
expect(typeof rootpolicy).toBe("string");
|
|
17
|
-
expect(manifest.browsers?.firefox?.content_security_policy?.extension_pages).toBe(rootpolicy);
|
|
18
|
-
expect(manifest.browsers?.safari?.content_security_policy?.extension_pages).toBe(rootpolicy);
|
|
19
|
-
/* the gate script reads the same three policies the manifest declares */
|
|
20
|
-
const gate = await readFile("tests/cspaudit.mjs", "utf8");
|
|
21
|
-
expect(gate).toContain("manifest.content_security_policy?.extension_pages");
|
|
22
|
-
expect(gate).toContain("browsers?.[browser]?.content_security_policy?.extension_pages");
|
|
23
|
-
for (const browser of ["firefox", "safari"]) expect(gate).toContain(`"${browser}"`);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("pins every script, object and frame source of every policy to the local origin", async () => {
|
|
27
|
-
const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as manifestshape;
|
|
28
|
-
const policies = [
|
|
29
|
-
manifest.content_security_policy?.extension_pages,
|
|
30
|
-
manifest.browsers?.firefox?.content_security_policy?.extension_pages,
|
|
31
|
-
manifest.browsers?.safari?.content_security_policy?.extension_pages,
|
|
32
|
-
].filter((policy): policy is string => typeof policy === "string");
|
|
33
|
-
expect(policies).toHaveLength(3);
|
|
34
|
-
for (const policy of policies) {
|
|
35
|
-
const directives = new Map<string, string[]>();
|
|
36
|
-
for (const directive of policy
|
|
37
|
-
.split(";")
|
|
38
|
-
.map((part) => part.trim())
|
|
39
|
-
.filter((part) => part !== "")) {
|
|
40
|
-
const [name, ...sources] = directive.split(/\s+/);
|
|
41
|
-
directives.set(name ?? "", sources);
|
|
42
|
-
}
|
|
43
|
-
expect(directives.get("script-src")).toEqual(["'self'"]);
|
|
44
|
-
expect(directives.get("object-src")).toEqual(["'self'"]);
|
|
45
|
-
expect(directives.get("frame-ancestors")).toEqual(["'self'"]);
|
|
46
|
-
for (const sources of directives.values())
|
|
47
|
-
for (const source of sources) {
|
|
48
|
-
expect(source.includes("*")).toBe(false);
|
|
49
|
-
expect(source.startsWith("http:")).toBe(false);
|
|
50
|
-
expect(source.startsWith("https:")).toBe(false);
|
|
51
|
-
expect(source.includes("unsafe-eval")).toBe(false);
|
|
52
|
-
expect(source.includes("unsafe-inline")).toBe(false);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("declares the sandbox frame the dashboard renders untrusted extracts inside", async () => {
|
|
58
|
-
const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as manifestshape;
|
|
59
|
-
expect(manifest.sandbox?.pages).toContain("sandbox.html");
|
|
60
|
-
const webindex = await readFile("web/design.html", "utf8");
|
|
61
|
-
const sandboxmatch = /<template data-surface="sandbox">([\s\S]*?)<\/template>/.exec(webindex);
|
|
62
|
-
expect(sandboxmatch).not.toBeNull();
|
|
63
|
-
expect(sandboxmatch?.[1]).toContain('setAttribute("sandbox", "")');
|
|
64
|
-
expect(sandboxmatch?.[1]).toContain("devthinksandbox");
|
|
65
|
-
const dashboardmatch = /<template data-surface="dashboardpage">([\s\S]*?)<\/template>/.exec(webindex);
|
|
66
|
-
expect(dashboardmatch).not.toBeNull();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("keeps every extension page free of remote resources and inline event handlers", async () => {
|
|
70
|
-
const webindex = await readFile("web/design.html", "utf8");
|
|
71
|
-
for (const reference of [...webindex.matchAll(/(?:src|href)\s*=\s*["']([^"']+)["']/g)].map(
|
|
72
|
-
(match) => match[1] ?? "",
|
|
73
|
-
)) {
|
|
74
|
-
if (/^(https?|wss?|data):/i.test(reference) && !reference.startsWith("blob:"))
|
|
75
|
-
throw new Error(`The web design loads the remote resource ${reference}; every extension page stays local.`);
|
|
76
|
-
}
|
|
77
|
-
expect(/\son[a-z]+\s*=\s*["']/i.test(webindex)).toBe(false);
|
|
78
|
-
});
|
|
79
|
-
});
|
package/tests/dashdone.test.ts
DELETED
|
@@ -1,615 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
import { sessionmemory } from "../memory.js";
|
|
4
|
-
import { clamppanelwidth, panelwidthbounds, panelwidthslayout, panelwidthsof, resizepanel } from "../views.js";
|
|
5
|
-
import {
|
|
6
|
-
agentcontrolsof,
|
|
7
|
-
aggregatereportdownloadof,
|
|
8
|
-
conflictlogof,
|
|
9
|
-
costperagentof,
|
|
10
|
-
dashboardemptyguidanceof,
|
|
11
|
-
dashboardviewerof,
|
|
12
|
-
escalationinboxof,
|
|
13
|
-
messageflowof,
|
|
14
|
-
multiagentoverviewof,
|
|
15
|
-
multiagentviewof,
|
|
16
|
-
queuelaneviewof,
|
|
17
|
-
timelinescrubof,
|
|
18
|
-
topologyinventoryof,
|
|
19
|
-
} from "../dashdone.js";
|
|
20
|
-
import type {
|
|
21
|
-
agentidentity,
|
|
22
|
-
agentmailbox,
|
|
23
|
-
agentusage,
|
|
24
|
-
conflictscan,
|
|
25
|
-
escalationrecord,
|
|
26
|
-
killswitch,
|
|
27
|
-
leaderworker,
|
|
28
|
-
resourcelock,
|
|
29
|
-
resultreport,
|
|
30
|
-
taskqueue,
|
|
31
|
-
swarmaction,
|
|
32
|
-
swarmcost,
|
|
33
|
-
} from "../types.js";
|
|
34
|
-
|
|
35
|
-
const now = 1_800_000_000_000;
|
|
36
|
-
|
|
37
|
-
/** The in memory adapter the panel width persistence round trip rides: the layout record the resize writes reads back through the same seam. */
|
|
38
|
-
class fakeadapter {
|
|
39
|
-
private readonly data = new Map<string, unknown>();
|
|
40
|
-
async get<T>(key: string): Promise<T | undefined> {
|
|
41
|
-
return this.data.get(key) as T | undefined;
|
|
42
|
-
}
|
|
43
|
-
async set<T>(key: string, value: T): Promise<void> {
|
|
44
|
-
this.data.set(key, value);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Builds the five agent fleet of the certified leader worker topology. */
|
|
49
|
-
function fleet(): agentidentity[] {
|
|
50
|
-
return [
|
|
51
|
-
{ id: "a1", name: "Scout", role: "worker", depth: 0, state: "active", tabid: 101, registeredat: now },
|
|
52
|
-
{ id: "w1", name: "Scribe", role: "worker", depth: 0, state: "active", tabid: 102, registeredat: now },
|
|
53
|
-
{ id: "w2", name: "Probe", role: "worker", depth: 0, state: "paused", tabid: 201, registeredat: now },
|
|
54
|
-
{ id: "c1", name: "Judge", role: "critic", depth: 0, state: "active", tabid: 202, registeredat: now },
|
|
55
|
-
{ id: "v1", name: "Witness", role: "verifier", depth: 0, state: "stopped", tabid: 301, registeredat: now },
|
|
56
|
-
];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** Builds the shared queue of the certification with both lanes and one claim. */
|
|
60
|
-
function queue(): taskqueue {
|
|
61
|
-
return {
|
|
62
|
-
lanes: ["extraction", "indexing"],
|
|
63
|
-
priorities: [2, 1],
|
|
64
|
-
completionpolicy: "all",
|
|
65
|
-
items: [
|
|
66
|
-
{
|
|
67
|
-
id: "t1",
|
|
68
|
-
lane: "extraction",
|
|
69
|
-
priority: 2,
|
|
70
|
-
payload: "Read the pricing table",
|
|
71
|
-
state: "claimed",
|
|
72
|
-
enqueuedat: now,
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
id: "t2",
|
|
76
|
-
lane: "indexing",
|
|
77
|
-
priority: 1,
|
|
78
|
-
payload: "Index the footer links",
|
|
79
|
-
state: "queued",
|
|
80
|
-
enqueuedat: now + 1,
|
|
81
|
-
},
|
|
82
|
-
{ id: "t3", lane: "extraction", priority: 1, payload: "Read the hero", state: "done", enqueuedat: now + 2 },
|
|
83
|
-
],
|
|
84
|
-
claims: [
|
|
85
|
-
{ agentid: "w1", taskid: "t1", claimedat: now, heartbeatat: now },
|
|
86
|
-
{ agentid: "w1", taskid: "t3", claimedat: now + 2, heartbeatat: now + 3 },
|
|
87
|
-
],
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Builds the mailboxes of the fleet with one unread direct message. */
|
|
92
|
-
function mailboxes(): agentmailbox[] {
|
|
93
|
-
return [
|
|
94
|
-
{
|
|
95
|
-
agentid: "w1",
|
|
96
|
-
inbox: [
|
|
97
|
-
{
|
|
98
|
-
id: "m1",
|
|
99
|
-
senderid: "a1",
|
|
100
|
-
recipient: "w1",
|
|
101
|
-
routing: "direct",
|
|
102
|
-
payload: "Start with the table.",
|
|
103
|
-
sentat: now + 1,
|
|
104
|
-
},
|
|
105
|
-
],
|
|
106
|
-
outbox: [],
|
|
107
|
-
unread: 1,
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
agentid: "a1",
|
|
111
|
-
inbox: [],
|
|
112
|
-
outbox: [
|
|
113
|
-
{
|
|
114
|
-
id: "m1",
|
|
115
|
-
senderid: "a1",
|
|
116
|
-
recipient: "w1",
|
|
117
|
-
routing: "direct",
|
|
118
|
-
payload: "Start with the table.",
|
|
119
|
-
sentat: now + 1,
|
|
120
|
-
readat: now + 2,
|
|
121
|
-
},
|
|
122
|
-
],
|
|
123
|
-
unread: 0,
|
|
124
|
-
},
|
|
125
|
-
];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
describe("the multi agent dashboard view panels", () => {
|
|
129
|
-
it("shapes the overview panel with the topology status, the state counts and the killswitch", () => {
|
|
130
|
-
const topology: leaderworker = {
|
|
131
|
-
id: "top1",
|
|
132
|
-
leaderid: "a1",
|
|
133
|
-
workerids: ["w1", "w2"],
|
|
134
|
-
criticids: ["c1"],
|
|
135
|
-
verifierids: ["v1"],
|
|
136
|
-
assignments: [],
|
|
137
|
-
rule: { kind: "first" },
|
|
138
|
-
electedat: now,
|
|
139
|
-
};
|
|
140
|
-
const overview = multiagentoverviewof({
|
|
141
|
-
agents: fleet(),
|
|
142
|
-
queue: queue(),
|
|
143
|
-
topology,
|
|
144
|
-
killswitch: { engaged: true, engagedat: now, reason: "The user halted the swarm." },
|
|
145
|
-
unread: 3,
|
|
146
|
-
});
|
|
147
|
-
expect(overview.topology).toContain("leader a1");
|
|
148
|
-
expect(overview.workers).toBe(2);
|
|
149
|
-
expect(overview.critics).toBe(1);
|
|
150
|
-
expect(overview.verifiers).toBe(1);
|
|
151
|
-
expect(overview.killswitch).toContain("engaged");
|
|
152
|
-
expect(overview.agents).toBe(5);
|
|
153
|
-
expect(overview.active).toBe(3);
|
|
154
|
-
expect(overview.paused).toBe(1);
|
|
155
|
-
expect(overview.stopped).toBe(1);
|
|
156
|
-
expect(overview).toMatchObject({ tasks: 3, queued: 1, claimed: 1, done: 1, unread: 3 });
|
|
157
|
-
const bare = multiagentoverviewof({
|
|
158
|
-
agents: [],
|
|
159
|
-
queue: { lanes: [], priorities: [], completionpolicy: "all", items: [], claims: [] },
|
|
160
|
-
killswitch: { engaged: false },
|
|
161
|
-
unread: 0,
|
|
162
|
-
});
|
|
163
|
-
expect(bare.topology).toBe("no topology elected");
|
|
164
|
-
expect(bare.killswitch).toBe("kill switch disarmed");
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it("shapes the per agent status cards with the live progress and the usage counters", () => {
|
|
168
|
-
const view = multiagentviewof({
|
|
169
|
-
agents: fleet(),
|
|
170
|
-
queue: queue(),
|
|
171
|
-
mailboxes: mailboxes(),
|
|
172
|
-
killswitch: { engaged: false },
|
|
173
|
-
usage: [{ agentid: "w1", tokens: 280, cost: 0.028, steps: 2, updatedat: now }],
|
|
174
|
-
costs: [],
|
|
175
|
-
conflicts: [],
|
|
176
|
-
locks: [],
|
|
177
|
-
escalations: [],
|
|
178
|
-
timeline: [],
|
|
179
|
-
now,
|
|
180
|
-
});
|
|
181
|
-
const scribe = view.cards.find((card) => card.agentid === "w1");
|
|
182
|
-
expect(scribe).toMatchObject({
|
|
183
|
-
name: "Scribe",
|
|
184
|
-
role: "worker",
|
|
185
|
-
state: "active",
|
|
186
|
-
tabid: 102,
|
|
187
|
-
depth: 0,
|
|
188
|
-
currenttask: "Read the pricing table",
|
|
189
|
-
claimed: 1,
|
|
190
|
-
done: 1,
|
|
191
|
-
tokens: 280,
|
|
192
|
-
cost: 0.028,
|
|
193
|
-
steps: 2,
|
|
194
|
-
unread: 1,
|
|
195
|
-
});
|
|
196
|
-
const witness = view.cards.find((card) => card.agentid === "v1");
|
|
197
|
-
expect(witness?.tokens).toBe(0);
|
|
198
|
-
expect(witness?.unread).toBe(0);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it("shapes the shared queue view with the lane filter and the empty lane state", () => {
|
|
202
|
-
const view = multiagentviewof({
|
|
203
|
-
agents: fleet(),
|
|
204
|
-
queue: queue(),
|
|
205
|
-
mailboxes: mailboxes(),
|
|
206
|
-
killswitch: { engaged: false },
|
|
207
|
-
usage: [],
|
|
208
|
-
costs: [],
|
|
209
|
-
conflicts: [],
|
|
210
|
-
locks: [],
|
|
211
|
-
escalations: [],
|
|
212
|
-
timeline: [],
|
|
213
|
-
now,
|
|
214
|
-
});
|
|
215
|
-
expect(view.queueview.lanes).toEqual(["extraction", "indexing"]);
|
|
216
|
-
expect(view.queueview.rows).toHaveLength(3);
|
|
217
|
-
expect(view.queueview.rows[0]).toMatchObject({
|
|
218
|
-
taskid: "t1",
|
|
219
|
-
lane: "extraction",
|
|
220
|
-
priority: 2,
|
|
221
|
-
state: "claimed",
|
|
222
|
-
holder: "w1",
|
|
223
|
-
});
|
|
224
|
-
const filtered = queuelaneviewof({ queue: queue(), lane: "indexing" });
|
|
225
|
-
expect(filtered.lane).toBe("indexing");
|
|
226
|
-
expect(filtered.rows.map((row) => row.taskid)).toEqual(["t2"]);
|
|
227
|
-
const emptylane = queuelaneviewof({
|
|
228
|
-
queue: { lanes: ["extraction"], priorities: [1], completionpolicy: "all", items: [], claims: [] },
|
|
229
|
-
lane: "extraction",
|
|
230
|
-
});
|
|
231
|
-
expect(emptylane.rows).toEqual([]);
|
|
232
|
-
const unfiltered = queuelaneviewof({ queue: queue(), lane: " " });
|
|
233
|
-
expect(unfiltered.rows).toHaveLength(3);
|
|
234
|
-
expect(unfiltered.lane).toBeUndefined();
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
it("shapes the message flow view as one time ordered stream between the agents", () => {
|
|
238
|
-
const flow = messageflowof({ mailboxes: mailboxes() });
|
|
239
|
-
expect(flow.deliveries).toBe(2);
|
|
240
|
-
expect(flow.rows[0]).toMatchObject({
|
|
241
|
-
messageid: "m1",
|
|
242
|
-
senderid: "a1",
|
|
243
|
-
recipient: "w1",
|
|
244
|
-
routing: "direct inbox",
|
|
245
|
-
payload: "Start with the table.",
|
|
246
|
-
read: false,
|
|
247
|
-
});
|
|
248
|
-
expect(flow.rows[1]?.routing).toBe("direct outbox");
|
|
249
|
-
expect(flow.rows[1]?.read).toBe(true);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it("shapes the conflict and arbitration log from the scans and the live locks", () => {
|
|
253
|
-
const scan: conflictscan = {
|
|
254
|
-
id: "scan1",
|
|
255
|
-
writers: [
|
|
256
|
-
{ agentid: "w1", origin: "https://example.com", selector: "#submit" },
|
|
257
|
-
{ agentid: "w2", origin: "https://example.com", selector: "#submit" },
|
|
258
|
-
],
|
|
259
|
-
overlaps: [{ origin: "https://example.com", selector: "#submit", writers: ["w1", "w2"] }],
|
|
260
|
-
suggestedorder: ["w1", "w2"],
|
|
261
|
-
clean: false,
|
|
262
|
-
scannedat: now,
|
|
263
|
-
};
|
|
264
|
-
const lock: resourcelock = {
|
|
265
|
-
key: "https://example.com|#submit",
|
|
266
|
-
holder: "w1",
|
|
267
|
-
kind: "exclusive",
|
|
268
|
-
origin: "https://example.com",
|
|
269
|
-
selector: "#submit",
|
|
270
|
-
acquiredat: now + 1,
|
|
271
|
-
};
|
|
272
|
-
const log = conflictlogof({ conflicts: [scan], locks: [lock] });
|
|
273
|
-
expect(log.open).toBe(1);
|
|
274
|
-
expect(log.rows).toHaveLength(2);
|
|
275
|
-
expect(log.rows[0]).toMatchObject({ source: "lock", key: "https://example.com|#submit" });
|
|
276
|
-
expect(log.rows[1]).toMatchObject({
|
|
277
|
-
source: "conflict",
|
|
278
|
-
key: "https://example.com|#submit",
|
|
279
|
-
writers: ["w1", "w2"],
|
|
280
|
-
});
|
|
281
|
-
const clean = conflictlogof({
|
|
282
|
-
conflicts: [
|
|
283
|
-
{
|
|
284
|
-
id: "scan2",
|
|
285
|
-
writers: [{ agentid: "w1", origin: "https://example.com", selector: "#a" }],
|
|
286
|
-
overlaps: [],
|
|
287
|
-
suggestedorder: [],
|
|
288
|
-
clean: true,
|
|
289
|
-
scannedat: now,
|
|
290
|
-
},
|
|
291
|
-
],
|
|
292
|
-
locks: [],
|
|
293
|
-
});
|
|
294
|
-
expect(clean.open).toBe(0);
|
|
295
|
-
expect(clean.rows[0]?.detail).toContain("clean");
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it("shapes the cost per agent panel from the accounting the costcert totals feed", () => {
|
|
299
|
-
const usage: agentusage[] = [
|
|
300
|
-
{ agentid: "w2", tokens: 90, cost: 0.009, steps: 1, updatedat: now },
|
|
301
|
-
{ agentid: "w1", tokens: 280, cost: 0.028, steps: 2, updatedat: now },
|
|
302
|
-
];
|
|
303
|
-
const totals: swarmcost = { agents: 2, tokens: 370, cost: 0.037, steps: 3, currency: "usd", computedat: now };
|
|
304
|
-
const costview = costperagentof({ usage, totals });
|
|
305
|
-
expect(costview.rows.map((row) => row.agentid)).toEqual(["w1", "w2"]);
|
|
306
|
-
expect(costview.rows[0]).toMatchObject({
|
|
307
|
-
tokens: 280,
|
|
308
|
-
cost: 0.028,
|
|
309
|
-
steps: 2,
|
|
310
|
-
share: Math.round((280 / 370) * 100) / 100,
|
|
311
|
-
});
|
|
312
|
-
expect(costview.total).toMatchObject({ tokens: 370, cost: 0.037, steps: 3, currency: "usd" });
|
|
313
|
-
expect(costview.source).toContain("costcert");
|
|
314
|
-
const empty = costperagentof({ usage: [] });
|
|
315
|
-
expect(empty.rows).toEqual([]);
|
|
316
|
-
expect(empty.total.tokens).toBe(0);
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
it("shapes the escalation inbox with the open count and the decided rows", () => {
|
|
320
|
-
const escalations: escalationrecord[] = [
|
|
321
|
-
{
|
|
322
|
-
id: "e1",
|
|
323
|
-
agentid: "w2",
|
|
324
|
-
subject: "The paywall blocks the footer.",
|
|
325
|
-
context: "The read needs the user decision.",
|
|
326
|
-
state: "open",
|
|
327
|
-
raisedat: now,
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
id: "e2",
|
|
331
|
-
agentid: "w1",
|
|
332
|
-
subject: "Two rows disagree.",
|
|
333
|
-
context: "The merge needs the policy order.",
|
|
334
|
-
state: "decided",
|
|
335
|
-
decision: "Keep the re-read count.",
|
|
336
|
-
raisedat: now - 1,
|
|
337
|
-
decidedat: now,
|
|
338
|
-
},
|
|
339
|
-
];
|
|
340
|
-
const inbox = escalationinboxof({ escalations });
|
|
341
|
-
expect(inbox.open).toBe(1);
|
|
342
|
-
expect(inbox.rows[0]).toMatchObject({ escalationid: "e1", agentid: "w2", state: "open" });
|
|
343
|
-
expect(inbox.rows[0]?.decision).toBeUndefined();
|
|
344
|
-
expect(inbox.rows[1]).toMatchObject({ state: "decided", decision: "Keep the re-read count." });
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
it("shapes the timeline scrubber over the interleaved events with the positions", () => {
|
|
348
|
-
const actions: swarmaction[] = [
|
|
349
|
-
{ id: "x2", kind: "readtext", agentid: "w2", summary: "The background agent read the table.", at: now + 300 },
|
|
350
|
-
{
|
|
351
|
-
id: "x1",
|
|
352
|
-
kind: "click",
|
|
353
|
-
agentid: "w1",
|
|
354
|
-
summary: "The interactive agent opened the pricing page.",
|
|
355
|
-
at: now + 100,
|
|
356
|
-
},
|
|
357
|
-
];
|
|
358
|
-
const timeline = timelinescrubof({ actions });
|
|
359
|
-
expect(timeline.marks.map((mark) => mark.actionid)).toEqual(["x1", "x2"]);
|
|
360
|
-
expect(timeline.marks[0]?.position).toBe(0);
|
|
361
|
-
expect(timeline.marks[1]?.position).toBe(1);
|
|
362
|
-
expect(timeline.position).toBe(1);
|
|
363
|
-
const positioned = timelinescrubof({ actions, position: 0 });
|
|
364
|
-
expect(positioned.position).toBe(0);
|
|
365
|
-
const empty = timelinescrubof({ actions: [] });
|
|
366
|
-
expect(empty.marks).toEqual([]);
|
|
367
|
-
expect(empty.position).toBe(0);
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
it("shapes the aggregate report download and its empty state", () => {
|
|
371
|
-
const report: resultreport = {
|
|
372
|
-
id: "rep1",
|
|
373
|
-
title: "The pricing table read",
|
|
374
|
-
sections: [
|
|
375
|
-
{
|
|
376
|
-
title: "rowcount",
|
|
377
|
-
entries: [{ id: "m1", agentid: "w1", taskid: "t1", key: "rowcount", value: "14", mergedat: now }],
|
|
378
|
-
sources: ["w1"],
|
|
379
|
-
},
|
|
380
|
-
],
|
|
381
|
-
sources: ["w1", "w2"],
|
|
382
|
-
confidence: "The verifier re-read both counts.",
|
|
383
|
-
createdat: now,
|
|
384
|
-
};
|
|
385
|
-
const download = aggregatereportdownloadof({ report, now: now + 10 });
|
|
386
|
-
expect(download.filename).toBe("devthink-aggregate-rep1.json");
|
|
387
|
-
expect(download.bytes).toBeGreaterThan(10);
|
|
388
|
-
expect(JSON.parse(download.payload)).toMatchObject({ id: "rep1", title: "The pricing table read" });
|
|
389
|
-
expect(download.reason).toContain("2 contributing agents");
|
|
390
|
-
const absent = aggregatereportdownloadof({ now });
|
|
391
|
-
expect(absent).toMatchObject({ filename: "", payload: "", bytes: 0 });
|
|
392
|
-
expect(absent.reason).toContain("No aggregate report exists yet");
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
it("shapes the per agent controls the viewer role and the killswitch gate", () => {
|
|
396
|
-
const active = fleet()[1]!;
|
|
397
|
-
expect(
|
|
398
|
-
agentcontrolsof({ agent: active, viewer: { readonly: false }, killswitch: { engaged: false } }),
|
|
399
|
-
).toMatchObject({ pause: true, resume: false, stop: true, killswitch: true });
|
|
400
|
-
const paused = fleet()[2]!;
|
|
401
|
-
expect(
|
|
402
|
-
agentcontrolsof({ agent: paused, viewer: { readonly: false }, killswitch: { engaged: false } }),
|
|
403
|
-
).toMatchObject({ pause: false, resume: true, stop: true });
|
|
404
|
-
expect(
|
|
405
|
-
agentcontrolsof({ agent: active, viewer: { readonly: true }, killswitch: { engaged: false } }).reason,
|
|
406
|
-
).toMatch(/no run role/);
|
|
407
|
-
expect(
|
|
408
|
-
agentcontrolsof({ agent: active, viewer: { readonly: false }, killswitch: { engaged: true, engagedat: now } })
|
|
409
|
-
.reason,
|
|
410
|
-
).toMatch(/kill switch stays engaged/);
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
it("renders read only for observers without the run role and keeps the controls for the operator", () => {
|
|
414
|
-
expect(dashboardviewerof({})).toMatchObject({ readonly: false, controls: true });
|
|
415
|
-
expect(dashboardviewerof({ viewer: { role: "worker" } }).controls).toBe(true);
|
|
416
|
-
const observer = dashboardviewerof({ viewer: { role: "observer" } });
|
|
417
|
-
expect(observer.readonly).toBe(true);
|
|
418
|
-
expect(observer.controls).toBe(false);
|
|
419
|
-
expect(observer.reason).toMatch(/renders read only/);
|
|
420
|
-
expect(dashboardviewerof({ viewer: { role: "worker", readonly: true } }).controls).toBe(false);
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
it("shapes the empty state guidance that walks the first multi agent run", () => {
|
|
424
|
-
const empty = dashboardemptyguidanceof({ agents: [] });
|
|
425
|
-
expect(empty.empty).toBe(true);
|
|
426
|
-
expect(empty.guidance.length).toBeGreaterThanOrEqual(4);
|
|
427
|
-
expect(empty.guidance.join(" ")).toMatch(/register/i);
|
|
428
|
-
expect(empty.guidance.join(" ")).toMatch(/lanes/i);
|
|
429
|
-
expect(dashboardemptyguidanceof({ agents: fleet() })).toEqual({ empty: false, guidance: [] });
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
it("shapes the topology inventory of the agent registry snapshot", () => {
|
|
433
|
-
const topology: leaderworker = {
|
|
434
|
-
id: "top1",
|
|
435
|
-
leaderid: "a1",
|
|
436
|
-
workerids: ["w1", "w2"],
|
|
437
|
-
criticids: ["c1"],
|
|
438
|
-
verifierids: ["v1"],
|
|
439
|
-
assignments: [],
|
|
440
|
-
rule: { kind: "first" },
|
|
441
|
-
electedat: now,
|
|
442
|
-
};
|
|
443
|
-
const inventory = topologyinventoryof({ agents: fleet(), topology });
|
|
444
|
-
expect(inventory.entries).toHaveLength(5);
|
|
445
|
-
expect(inventory.scenarios).toBe(6);
|
|
446
|
-
expect(inventory.certified).toBe(true);
|
|
447
|
-
expect(inventory.entries.find((entry) => entry.agentid === "a1")?.lane).toBe("leader");
|
|
448
|
-
expect(inventory.entries.find((entry) => entry.agentid === "c1")?.lane).toBe("critic");
|
|
449
|
-
const bare = topologyinventoryof({ agents: [] });
|
|
450
|
-
expect(bare.certified).toBe(false);
|
|
451
|
-
expect(bare.reason).toMatch(/no agent/i);
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
it("shapes the full multi agent view with every panel in one call", () => {
|
|
455
|
-
const topology: leaderworker = {
|
|
456
|
-
id: "top1",
|
|
457
|
-
leaderid: "a1",
|
|
458
|
-
workerids: ["w1", "w2"],
|
|
459
|
-
criticids: ["c1"],
|
|
460
|
-
verifierids: ["v1"],
|
|
461
|
-
assignments: [],
|
|
462
|
-
rule: { kind: "first" },
|
|
463
|
-
electedat: now,
|
|
464
|
-
};
|
|
465
|
-
const view = multiagentviewof({
|
|
466
|
-
agents: fleet(),
|
|
467
|
-
queue: queue(),
|
|
468
|
-
mailboxes: mailboxes(),
|
|
469
|
-
topology,
|
|
470
|
-
killswitch: { engaged: false },
|
|
471
|
-
usage: [{ agentid: "w1", tokens: 280, cost: 0.028, steps: 2, updatedat: now }],
|
|
472
|
-
costs: [{ agents: 1, tokens: 280, cost: 0.028, steps: 2, computedat: now }],
|
|
473
|
-
conflicts: [],
|
|
474
|
-
locks: [],
|
|
475
|
-
escalations: [
|
|
476
|
-
{
|
|
477
|
-
id: "e1",
|
|
478
|
-
agentid: "w2",
|
|
479
|
-
subject: "The paywall blocks the footer.",
|
|
480
|
-
context: "The read needs the user decision.",
|
|
481
|
-
state: "open",
|
|
482
|
-
raisedat: now,
|
|
483
|
-
},
|
|
484
|
-
],
|
|
485
|
-
timeline: [
|
|
486
|
-
{ id: "x1", kind: "click", agentid: "w1", summary: "The interactive agent opened the pricing page.", at: now },
|
|
487
|
-
],
|
|
488
|
-
report: {
|
|
489
|
-
id: "rep1",
|
|
490
|
-
title: "The pricing table read",
|
|
491
|
-
sections: [
|
|
492
|
-
{
|
|
493
|
-
title: "rowcount",
|
|
494
|
-
entries: [{ id: "m1", agentid: "w1", taskid: "t1", key: "rowcount", value: "14", mergedat: now }],
|
|
495
|
-
sources: ["w1"],
|
|
496
|
-
},
|
|
497
|
-
],
|
|
498
|
-
sources: ["w1"],
|
|
499
|
-
createdat: now,
|
|
500
|
-
},
|
|
501
|
-
viewer: { role: "observer" },
|
|
502
|
-
lane: "extraction",
|
|
503
|
-
now,
|
|
504
|
-
});
|
|
505
|
-
expect(view.overview.leader).toBe("a1");
|
|
506
|
-
expect(view.cards).toHaveLength(5);
|
|
507
|
-
expect(view.queueview.lane).toBe("extraction");
|
|
508
|
-
expect(view.queueview.rows).toHaveLength(2);
|
|
509
|
-
expect(view.messageflow.deliveries).toBe(2);
|
|
510
|
-
expect(view.escalationinbox.open).toBe(1);
|
|
511
|
-
expect(view.timeline.marks).toHaveLength(1);
|
|
512
|
-
expect(view.reportdownload.filename).toBe("devthink-aggregate-rep1.json");
|
|
513
|
-
expect(view.viewer.readonly).toBe(true);
|
|
514
|
-
expect(view.controls.killswitch).toBe(false);
|
|
515
|
-
expect(view.inventory.certified).toBe(true);
|
|
516
|
-
});
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
describe("the dashboard panel resize of the 2.0.2 final polish", () => {
|
|
520
|
-
it("clamps the panel column widths inside the readable minimum and the shared maximum", () => {
|
|
521
|
-
expect(panelwidthbounds).toEqual({ minimum: 280, maximum: 720 });
|
|
522
|
-
expect(clamppanelwidth(480)).toBe(480);
|
|
523
|
-
expect(clamppanelwidth(120)).toBe(280);
|
|
524
|
-
expect(clamppanelwidth(-40)).toBe(280);
|
|
525
|
-
expect(clamppanelwidth(5_000)).toBe(720);
|
|
526
|
-
expect(clamppanelwidth(480.6)).toBe(481);
|
|
527
|
-
expect(clamppanelwidth(Number.NaN)).toBe(280);
|
|
528
|
-
/* a known viewport narrows the ceiling so the sibling column never loses its own minimum */
|
|
529
|
-
expect(clamppanelwidth(700, 900)).toBe(620);
|
|
530
|
-
expect(clamppanelwidth(300, 900)).toBe(300);
|
|
531
|
-
expect(clamppanelwidth(700, 400)).toBe(280);
|
|
532
|
-
/* the resize move applies a pointer delta through the clamp and names whether it clamped */
|
|
533
|
-
const open = resizepanel({ widths: { left: 480 }, delta: 60 });
|
|
534
|
-
expect(open.widths).toEqual({ left: 540 });
|
|
535
|
-
expect(open.clamped).toBe(false);
|
|
536
|
-
const floored = resizepanel({ widths: { left: 480 }, delta: -600 });
|
|
537
|
-
expect(floored.widths).toEqual({ left: 280 });
|
|
538
|
-
expect(floored.clamped).toBe(true);
|
|
539
|
-
const ceiled = resizepanel({ widths: { left: 700 }, delta: 400, viewport: 1_000 });
|
|
540
|
-
expect(ceiled.widths).toEqual({ left: 720 });
|
|
541
|
-
expect(ceiled.clamped).toBe(true);
|
|
542
|
-
const narrowing = resizepanel({ widths: { left: 600 }, delta: 400, viewport: 900 });
|
|
543
|
-
expect(narrowing.widths).toEqual({ left: 620 });
|
|
544
|
-
expect(narrowing.clamped).toBe(true);
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
it("round trips the panelwidths preference through the surface layout seam and clamps the stored row", async () => {
|
|
548
|
-
const store = new sessionmemory(new fakeadapter());
|
|
549
|
-
/* the serialize and parse pair keeps the settled split across the round trip while the clamp holds the stored row inside its bounds */
|
|
550
|
-
const widths = resizepanel({ widths: panelwidthsof(undefined), delta: 120 }).widths;
|
|
551
|
-
await store.setsurfacelayout({
|
|
552
|
-
surface: "dashboardpage",
|
|
553
|
-
preferences: { panelwidths: panelwidthslayout(widths) },
|
|
554
|
-
updatedat: now,
|
|
555
|
-
});
|
|
556
|
-
const stored = (await store.getsurfacelayout("dashboardpage"))?.preferences.panelwidths;
|
|
557
|
-
expect(stored).toBe(panelwidthslayout(widths));
|
|
558
|
-
expect(panelwidthsof(stored)).toEqual(widths);
|
|
559
|
-
/* an absent or malformed row keeps the shipped split while a wild value clamps back inside the bounds */
|
|
560
|
-
expect(panelwidthsof(undefined)).toEqual({ left: 480 });
|
|
561
|
-
expect(panelwidthsof("left=5")).toEqual({ left: 280 });
|
|
562
|
-
expect(panelwidthsof("left=99999")).toEqual({ left: 720 });
|
|
563
|
-
expect(panelwidthsof("nonsense")).toEqual({ left: 480 });
|
|
564
|
-
expect(panelwidthsof("left=640", 900)).toEqual({ left: 620 });
|
|
565
|
-
/* the dashboardpage renders the resize handle between the two panel columns with the pointer drag and the keyboard steps persisting the settled split */
|
|
566
|
-
const dashboardpage = await readFile("web/dashboardpage.ts", "utf8");
|
|
567
|
-
expect(dashboardpage).toContain("let panelwidths = panelwidthsof(undefined, viewportwidth());");
|
|
568
|
-
expect(dashboardpage).toContain('document.querySelector<HTMLElement>("#panelresizer")');
|
|
569
|
-
expect(dashboardpage).toContain('panelresizernode.addEventListener("pointerdown"');
|
|
570
|
-
expect(dashboardpage).toContain('panelresizernode.addEventListener("pointermove"');
|
|
571
|
-
expect(dashboardpage).toContain('panelresizernode.addEventListener("pointerup"');
|
|
572
|
-
expect(dashboardpage).toContain('panelresizernode.addEventListener("keydown"');
|
|
573
|
-
expect(dashboardpage).toContain(
|
|
574
|
-
"const move = resizepanel({ widths: dragstartwidths, delta: event.clientX - dragstartx,",
|
|
575
|
-
);
|
|
576
|
-
expect(dashboardpage).toContain(
|
|
577
|
-
"const preferences = { ...(layout.layout?.preferences ?? {}), panelwidths: panelwidthslayout(panelwidths) };",
|
|
578
|
-
);
|
|
579
|
-
const design = await readFile("web/design.html", "utf8");
|
|
580
|
-
expect(design).toContain(
|
|
581
|
-
'<div class="panelresizer" id="panelresizer" role="separator" aria-orientation="vertical"',
|
|
582
|
-
);
|
|
583
|
-
expect(design).toContain('aria-valuemin="280" aria-valuemax="720"');
|
|
584
|
-
for (const panel of [
|
|
585
|
-
"multiagentoverview",
|
|
586
|
-
"agentstatuscards",
|
|
587
|
-
"sharedqueueview",
|
|
588
|
-
"queuelanefilter",
|
|
589
|
-
"messageflow",
|
|
590
|
-
"conflictlog",
|
|
591
|
-
"agentcostpanel",
|
|
592
|
-
"escalationinbox",
|
|
593
|
-
"timelinescrubber",
|
|
594
|
-
"aggregatetimeline",
|
|
595
|
-
"killswitch",
|
|
596
|
-
"reportdownload",
|
|
597
|
-
"sessiongrid",
|
|
598
|
-
"flowlibrary",
|
|
599
|
-
"backgroundruns",
|
|
600
|
-
"historyhits",
|
|
601
|
-
"sitenotes",
|
|
602
|
-
"transparency",
|
|
603
|
-
"onboarding",
|
|
604
|
-
"perfsummary",
|
|
605
|
-
"perfchart",
|
|
606
|
-
"budgetalerts",
|
|
607
|
-
"selectorprofile",
|
|
608
|
-
"dropzone",
|
|
609
|
-
"historyquery",
|
|
610
|
-
"libraryquery",
|
|
611
|
-
]) {
|
|
612
|
-
expect(design).toContain(`id="${panel}"`);
|
|
613
|
-
}
|
|
614
|
-
});
|
|
615
|
-
});
|