@frockbot/plugin-shell 0.3.4 → 0.3.6
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/frockbot.json +1 -1
- package/package.json +31 -31
- package/src/backend-authoring.test.ts +72 -0
- package/src/backend-authoring.ts +82 -16
- package/src/backend-configuration.test.ts +6 -4
- package/src/backend-package-catalog.test.ts +23 -3
- package/src/backend-package-catalog.ts +7 -1
- package/src/backend-recovery-integration.test.ts +23 -23
- package/src/backend.ts +119 -29
- package/src/client/AppletCanvas.vue +3 -5
- package/src/client/FrockBotApp.vue +197 -79
- package/src/client/PackageIframeHost.vue +15 -6
- package/src/client/index.test.ts +111 -15
- package/src/client/index.ts +217 -43
- package/src/client/model-presentation.test.ts +4 -2
- package/src/client/model-presentation.ts +2 -2
- package/src/client/styles.css +76 -1
- package/src/run-protocol.test.ts +73 -0
- package/src/run-protocol.ts +161 -9
- package/src/shared.ts +6 -0
package/src/client/index.test.ts
CHANGED
|
@@ -516,7 +516,7 @@ describe("Bot selection", () => {
|
|
|
516
516
|
const requestCount = requests.length;
|
|
517
517
|
await expect(
|
|
518
518
|
provided.value.callPackageUiTool(contribution, "package_author", {}),
|
|
519
|
-
).rejects.toThrow("
|
|
519
|
+
).rejects.toThrow("isn't allowed to use");
|
|
520
520
|
expect(requests).toHaveLength(requestCount);
|
|
521
521
|
});
|
|
522
522
|
|
|
@@ -571,6 +571,47 @@ describe("Bot selection", () => {
|
|
|
571
571
|
expect(provided.value.settingsError).toBe("Bot settings unavailable");
|
|
572
572
|
});
|
|
573
573
|
|
|
574
|
+
test("says a deployment has no Catalog instead of relaying the gateway's sentence", async () => {
|
|
575
|
+
// F4: the Plugins/Catalog surface rendered `catalog generation was not
|
|
576
|
+
// found` beside "No Catalog entry matches that search", for a User who had
|
|
577
|
+
// searched nothing on a deployment that had published nothing.
|
|
578
|
+
let provided: Ref<FrockBotWebData> | undefined;
|
|
579
|
+
let failure = new Error("catalog generation was not found");
|
|
580
|
+
await shellClientPlugin({
|
|
581
|
+
transport: {
|
|
582
|
+
turn: () => Promise.resolve({ runId: "run", text: "", events: [] }),
|
|
583
|
+
readConfiguration: () => Promise.reject(new Error("unused")),
|
|
584
|
+
hostedRequest: (path) => {
|
|
585
|
+
expect(path).toBe("/catalog/v1/index");
|
|
586
|
+
return Promise.reject(failure);
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
slot: () => () => {},
|
|
590
|
+
inject: () => {
|
|
591
|
+
throw new Error("unexpected client provider injection");
|
|
592
|
+
},
|
|
593
|
+
provide: (_key, value) => {
|
|
594
|
+
provided = value as Ref<FrockBotWebData>;
|
|
595
|
+
return () => {};
|
|
596
|
+
},
|
|
597
|
+
});
|
|
598
|
+
if (!provided) throw new Error("shell data was not provided");
|
|
599
|
+
|
|
600
|
+
await provided.value.loadPackageCatalog();
|
|
601
|
+
expect(provided.value.packageCatalog).toEqual([]);
|
|
602
|
+
expect(provided.value.packageCatalogGeneration).toBeUndefined();
|
|
603
|
+
expect(provided.value.settingsError).toBe(
|
|
604
|
+
"No plugins are published for this deployment yet.",
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
// A genuine fault still reaches the operator, labelled as one.
|
|
608
|
+
failure = new Error("R2 is down");
|
|
609
|
+
await provided.value.loadPackageCatalog();
|
|
610
|
+
expect(provided.value.settingsError).toBe(
|
|
611
|
+
"Plugins could not be loaded: R2 is down",
|
|
612
|
+
);
|
|
613
|
+
});
|
|
614
|
+
|
|
574
615
|
test("commits a catalog without overwriting newer User settings", async () => {
|
|
575
616
|
let provided: Ref<FrockBotWebData> | undefined;
|
|
576
617
|
const catalogManifest = Promise.withResolvers<unknown>();
|
|
@@ -648,7 +689,7 @@ describe("Bot selection", () => {
|
|
|
648
689
|
},
|
|
649
690
|
accountValues: undefined,
|
|
650
691
|
platformModel: undefined,
|
|
651
|
-
expectedLabel: "Model name · Model provider · Bot
|
|
692
|
+
expectedLabel: "Model name · Model provider · this Bot only",
|
|
652
693
|
expectedProjection: "bot",
|
|
653
694
|
ready: true,
|
|
654
695
|
},
|
|
@@ -683,7 +724,7 @@ describe("Bot selection", () => {
|
|
|
683
724
|
botValues: {},
|
|
684
725
|
accountValues: undefined,
|
|
685
726
|
platformModel: undefined,
|
|
686
|
-
expectedLabel: "
|
|
727
|
+
expectedLabel: "No model available — set one up in Models",
|
|
687
728
|
expectedProjection: "none",
|
|
688
729
|
ready: false,
|
|
689
730
|
},
|
|
@@ -900,7 +941,7 @@ describe("Bot selection", () => {
|
|
|
900
941
|
|
|
901
942
|
expect(provided.value.modelReady).toBe(false);
|
|
902
943
|
expect(provided.value.modelLabel).toBe(
|
|
903
|
-
|
|
944
|
+
"That account needs reconnecting before this Bot can reply.",
|
|
904
945
|
);
|
|
905
946
|
});
|
|
906
947
|
|
|
@@ -1217,7 +1258,7 @@ describe("detached Turn projection", () => {
|
|
|
1217
1258
|
{ role: "user", status: "completed" },
|
|
1218
1259
|
{
|
|
1219
1260
|
role: "assistant",
|
|
1220
|
-
text: "
|
|
1261
|
+
text: "This Bot couldn't finish its reply. Try again.",
|
|
1221
1262
|
status: "error",
|
|
1222
1263
|
},
|
|
1223
1264
|
]);
|
|
@@ -1482,6 +1523,55 @@ describe("active durable Turn projection", () => {
|
|
|
1482
1523
|
]);
|
|
1483
1524
|
});
|
|
1484
1525
|
|
|
1526
|
+
test("a Turn that sent something draws the send, not the model's own text", () => {
|
|
1527
|
+
const state: Pick<
|
|
1528
|
+
FrockBotWebData,
|
|
1529
|
+
"messages" | "activeRunId" | "activeRun"
|
|
1530
|
+
> = { messages: [] };
|
|
1531
|
+
|
|
1532
|
+
projectDurableRuns(
|
|
1533
|
+
state,
|
|
1534
|
+
[],
|
|
1535
|
+
[
|
|
1536
|
+
{
|
|
1537
|
+
runId: "run-once",
|
|
1538
|
+
input: "ping",
|
|
1539
|
+
status: "completed",
|
|
1540
|
+
responseText: "pong",
|
|
1541
|
+
events: [
|
|
1542
|
+
{ type: "send/to-user", payload: { type: "text", text: "pong" } },
|
|
1543
|
+
],
|
|
1544
|
+
},
|
|
1545
|
+
],
|
|
1546
|
+
);
|
|
1547
|
+
|
|
1548
|
+
expect(state.messages[1]?.text).toBe("");
|
|
1549
|
+
expect(state.messages[1]?.sends).toHaveLength(1);
|
|
1550
|
+
});
|
|
1551
|
+
|
|
1552
|
+
test("a Turn that sent nothing still draws the model's text", () => {
|
|
1553
|
+
const state: Pick<
|
|
1554
|
+
FrockBotWebData,
|
|
1555
|
+
"messages" | "activeRunId" | "activeRun"
|
|
1556
|
+
> = { messages: [] };
|
|
1557
|
+
|
|
1558
|
+
projectDurableRuns(
|
|
1559
|
+
state,
|
|
1560
|
+
[],
|
|
1561
|
+
[
|
|
1562
|
+
{
|
|
1563
|
+
runId: "run-plain",
|
|
1564
|
+
input: "ping",
|
|
1565
|
+
status: "completed",
|
|
1566
|
+
responseText: "pong",
|
|
1567
|
+
events: [],
|
|
1568
|
+
},
|
|
1569
|
+
],
|
|
1570
|
+
);
|
|
1571
|
+
|
|
1572
|
+
expect(state.messages[1]?.text).toBe("pong");
|
|
1573
|
+
});
|
|
1574
|
+
|
|
1485
1575
|
test("streams running text without a banner and clears busy state", () => {
|
|
1486
1576
|
const state: Pick<
|
|
1487
1577
|
FrockBotWebData,
|
|
@@ -1562,11 +1652,11 @@ describe("active durable Turn projection", () => {
|
|
|
1562
1652
|
expect(reconciliation.activeRun).toEqual({
|
|
1563
1653
|
runId: "run-reconciliation",
|
|
1564
1654
|
status: "reconciliation-required",
|
|
1565
|
-
message: "
|
|
1655
|
+
message: "Something went wrong mid-reply. Try again to pick it up.",
|
|
1566
1656
|
canResume: true,
|
|
1567
1657
|
});
|
|
1568
1658
|
expect(reconciliation.messages[1]).toMatchObject({
|
|
1569
|
-
text: "
|
|
1659
|
+
text: "This reply stopped partway. Try again to continue it.",
|
|
1570
1660
|
status: "reconciliation-required",
|
|
1571
1661
|
});
|
|
1572
1662
|
});
|
|
@@ -1845,16 +1935,19 @@ describe("hosted Stop", () => {
|
|
|
1845
1935
|
await provided.value.stopRun();
|
|
1846
1936
|
expect(provided.value.activeRun).toMatchObject({
|
|
1847
1937
|
status: "reconciliation-required",
|
|
1848
|
-
message:
|
|
1849
|
-
|
|
1850
|
-
|
|
1938
|
+
message: "Stopping…",
|
|
1939
|
+
// A parked Turn is offered the resolve control, Stop included: hiding
|
|
1940
|
+
// it there hid it in exactly the case Stop creates.
|
|
1941
|
+
canResume: true,
|
|
1851
1942
|
});
|
|
1852
1943
|
|
|
1853
1944
|
await provided.value.stopRun();
|
|
1854
1945
|
expect(provided.value.activeRun).toBeUndefined();
|
|
1855
1946
|
expect(provided.value.activeRunId).toBeUndefined();
|
|
1947
|
+
// The Turn keeps whatever it had already said; the notice is the line
|
|
1948
|
+
// that says why it ends where it does.
|
|
1856
1949
|
expect(provided.value.messages[1]).toMatchObject({
|
|
1857
|
-
|
|
1950
|
+
notice: "You stopped this.",
|
|
1858
1951
|
status: "aborted",
|
|
1859
1952
|
});
|
|
1860
1953
|
|
|
@@ -1911,12 +2004,15 @@ describe("hosted Stop", () => {
|
|
|
1911
2004
|
|
|
1912
2005
|
await provided.value.stopRun();
|
|
1913
2006
|
|
|
1914
|
-
|
|
2007
|
+
// Two, and deliberately: a client that finds itself holding an active run
|
|
2008
|
+
// it did not start now observes it (a reload mid-Turn, a second tab), and
|
|
2009
|
+
// Stop then observes the settlement it asked for.
|
|
2010
|
+
expect(lookups).toBe(2);
|
|
1915
2011
|
expect(provided.value.activeRun).toBeUndefined();
|
|
1916
2012
|
expect(provided.value.activeRunId).toBeUndefined();
|
|
1917
2013
|
expect(provided.value.messages.at(-1)).toMatchObject({
|
|
1918
2014
|
runId: "run-1",
|
|
1919
|
-
|
|
2015
|
+
notice: "You stopped this.",
|
|
1920
2016
|
status: "aborted",
|
|
1921
2017
|
});
|
|
1922
2018
|
});
|
|
@@ -2506,7 +2602,7 @@ describe("Connection operation reconciliation", () => {
|
|
|
2506
2602
|
).rejects.toThrow("Connection state update failed");
|
|
2507
2603
|
await expect(
|
|
2508
2604
|
provided.value.disconnectConnection("connection-1"),
|
|
2509
|
-
).rejects.toThrow("
|
|
2605
|
+
).rejects.toThrow("Couldn't disconnect that account.");
|
|
2510
2606
|
expect(commands).toEqual([
|
|
2511
2607
|
"connection/update-label",
|
|
2512
2608
|
"connection/set-enabled",
|
|
@@ -2897,7 +2993,7 @@ describe("a message sent while a Turn is running", () => {
|
|
|
2897
2993
|
// The superseded Turn keeps the quiet treatment a stopped one gets.
|
|
2898
2994
|
expect(state.messages[1]).toMatchObject({
|
|
2899
2995
|
status: "aborted",
|
|
2900
|
-
|
|
2996
|
+
notice: "Interrupted by your next message.",
|
|
2901
2997
|
});
|
|
2902
2998
|
});
|
|
2903
2999
|
|