@antelopejs/dms-ai 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/lifecycle/skill-sources.js +19 -146
- package/dist/lifecycle/skill-sources.js.map +1 -1
- package/dist/pages/settings/availability.js +8 -1
- package/dist/pages/settings/availability.js.map +1 -1
- package/dist/pages/settings/options.d.ts +2 -1
- package/dist/pages/settings/options.js +29 -8
- package/dist/pages/settings/options.js.map +1 -1
- package/dist/pages/settings/sections.js +15 -4
- package/dist/pages/settings/sections.js.map +1 -1
- package/dist/routes/builder.d.ts +3 -3
- package/dist/routes/builder.js +7 -2
- package/dist/routes/builder.js.map +1 -1
- package/dist/routes/settings.js +1 -1
- package/dist/routes/settings.js.map +1 -1
- package/dist/sidecar/settings-values.d.ts +3 -0
- package/dist/sidecar/settings-values.js +33 -1
- package/dist/sidecar/settings-values.js.map +1 -1
- package/dist/vocabulary/enumerations.js +2 -0
- package/dist/vocabulary/enumerations.js.map +1 -1
- package/frontend-vue/app/chat/components/ChatView.vue +16 -49
- package/frontend-vue/app/chat/components/Composer.vue +23 -39
- package/frontend-vue/app/chat/components/NoticeCard.vue +9 -0
- package/frontend-vue/app/chat/components/ProviderNotice.vue +14 -3
- package/frontend-vue/app/chat/composables/conversation-events.ts +0 -3
- package/frontend-vue/app/chat/composables/useConversationMode.ts +8 -11
- package/frontend-vue/app/chat/composables/useFollowScroll.ts +77 -0
- package/frontend-vue/app/chat/constants/modes.ts +15 -0
- package/frontend-vue/app/chat/constants/protocol.ts +0 -1
- package/frontend-vue/app/chat/constants/settings.ts +1 -1
- package/frontend-vue/app/chat/types/protocol.ts +11 -9
- package/frontend-vue/app/chat/types/settings.ts +7 -2
- package/frontend-vue/app/chat/utils/permission-view.ts +1 -0
- package/frontend-vue/app/views/constants.ts +2 -0
- package/frontend-vue/app/views/types.ts +3 -1
- package/frontend-vue/i18n/locales/dms-ai-pages-en-GB.json +21 -11
- package/frontend-vue/i18n/locales/dms-ai-pages-fr-FR.json +21 -11
- package/frontend-vue/i18n/locales/dms-ai-panel-en-GB.json +11 -21
- package/frontend-vue/i18n/locales/dms-ai-panel-fr-FR.json +11 -21
- package/frontend-vue/i18n/locales/dms-ai-views-en-GB.json +2 -0
- package/frontend-vue/i18n/locales/dms-ai-views-fr-FR.json +2 -0
- package/frontend-vue/tests/chat/chat-scroll.test.ts +251 -0
- package/frontend-vue/tests/chat/conversation-events.test.ts +1 -3
- package/frontend-vue/tests/chat/provider-notice.test.ts +33 -0
- package/frontend-vue/tests/chat-panel.test.ts +2 -4
- package/frontend-vue/tests/panel-modes.test.ts +146 -0
- package/frontend-vue/tests/panel-questions.test.ts +1 -19
- package/frontend-vue/tests/panel-states.test.ts +3 -15
- package/frontend-vue/tests/support/fake-layout.ts +119 -0
- package/frontend-vue/tests/support/panel-harness.ts +14 -6
- package/frontend-vue/tests/support/setup-layout.ts +3 -0
- package/frontend-vue/tests/support/stubs.ts +2 -0
- package/frontend-vue/vitest.config.ts +1 -0
- package/package.json +4 -3
- package/sidecar/dist/agent/always-ask-files.d.ts +2 -0
- package/sidecar/dist/agent/always-ask-files.js +34 -0
- package/sidecar/dist/agent/always-ask-files.js.map +1 -0
- package/sidecar/dist/agent/conversation-modes.d.ts +3 -12
- package/sidecar/dist/agent/conversation-modes.js +16 -69
- package/sidecar/dist/agent/conversation-modes.js.map +1 -1
- package/sidecar/dist/agent/effective-mode.d.ts +2 -5
- package/sidecar/dist/agent/effective-mode.js +1 -15
- package/sidecar/dist/agent/effective-mode.js.map +1 -1
- package/sidecar/dist/agent/permission-bus.d.ts +1 -2
- package/sidecar/dist/agent/permission-bus.js +1 -3
- package/sidecar/dist/agent/permission-bus.js.map +1 -1
- package/sidecar/dist/agent/permission-preview.d.ts +6 -0
- package/sidecar/dist/agent/permission-preview.js +35 -10
- package/sidecar/dist/agent/permission-preview.js.map +1 -1
- package/sidecar/dist/agent/permission-rules.js +2 -2
- package/sidecar/dist/agent/permission-rules.js.map +1 -1
- package/sidecar/dist/agent/project-secrets.d.ts +7 -0
- package/sidecar/dist/agent/project-secrets.js +61 -0
- package/sidecar/dist/agent/project-secrets.js.map +1 -0
- package/sidecar/dist/agent/provider.d.ts +5 -4
- package/sidecar/dist/agent/runner.d.ts +3 -2
- package/sidecar/dist/agent/runner.js +9 -3
- package/sidecar/dist/agent/runner.js.map +1 -1
- package/sidecar/dist/agent/session.d.ts +3 -3
- package/sidecar/dist/agent/session.js.map +1 -1
- package/sidecar/dist/agent/tool-kinds.d.ts +7 -1
- package/sidecar/dist/agent/tool-kinds.js +24 -5
- package/sidecar/dist/agent/tool-kinds.js.map +1 -1
- package/sidecar/dist/audit/activity-query.js +2 -0
- package/sidecar/dist/audit/activity-query.js.map +1 -1
- package/sidecar/dist/audit/activity.js +1 -0
- package/sidecar/dist/audit/activity.js.map +1 -1
- package/sidecar/dist/builder/builder-client.js +13 -1
- package/sidecar/dist/builder/builder-client.js.map +1 -1
- package/sidecar/dist/constants/audit.d.ts +1 -3
- package/sidecar/dist/constants/audit.js +2 -1
- package/sidecar/dist/constants/audit.js.map +1 -1
- package/sidecar/dist/constants/builder.d.ts +3 -0
- package/sidecar/dist/constants/builder.js +3 -0
- package/sidecar/dist/constants/builder.js.map +1 -1
- package/sidecar/dist/constants/claude.d.ts +6 -0
- package/sidecar/dist/constants/claude.js +6 -0
- package/sidecar/dist/constants/claude.js.map +1 -1
- package/sidecar/dist/constants/codex.d.ts +4 -0
- package/sidecar/dist/constants/codex.js +4 -0
- package/sidecar/dist/constants/codex.js.map +1 -1
- package/sidecar/dist/constants/permissions.d.ts +2 -0
- package/sidecar/dist/constants/permissions.js +23 -0
- package/sidecar/dist/constants/permissions.js.map +1 -1
- package/sidecar/dist/constants/project-secrets.d.ts +5 -0
- package/sidecar/dist/constants/project-secrets.js +20 -0
- package/sidecar/dist/constants/project-secrets.js.map +1 -0
- package/sidecar/dist/constants/providers.d.ts +2 -0
- package/sidecar/dist/constants/providers.js +1 -0
- package/sidecar/dist/constants/providers.js.map +1 -1
- package/sidecar/dist/constants/settings.d.ts +1 -2
- package/sidecar/dist/constants/settings.js +2 -9
- package/sidecar/dist/constants/settings.js.map +1 -1
- package/sidecar/dist/constants/tool-kinds.d.ts +67 -2
- package/sidecar/dist/constants/tool-kinds.js +111 -34
- package/sidecar/dist/constants/tool-kinds.js.map +1 -1
- package/sidecar/dist/mcp/tools/builder-gate.js +3 -1
- package/sidecar/dist/mcp/tools/builder-gate.js.map +1 -1
- package/sidecar/dist/mcp/tools/builder-project.js +65 -3
- package/sidecar/dist/mcp/tools/builder-project.js.map +1 -1
- package/sidecar/dist/protocol/events.d.ts +97 -63
- package/sidecar/dist/protocol/events.js +14 -8
- package/sidecar/dist/protocol/events.js.map +1 -1
- package/sidecar/dist/protocol/messages.d.ts +54 -49
- package/sidecar/dist/protocol/messages.js +8 -13
- package/sidecar/dist/protocol/messages.js.map +1 -1
- package/sidecar/dist/providers/claude/config.d.ts +3 -3
- package/sidecar/dist/providers/claude/config.js +4 -2
- package/sidecar/dist/providers/claude/config.js.map +1 -1
- package/sidecar/dist/providers/claude/provider.js +20 -26
- package/sidecar/dist/providers/claude/provider.js.map +1 -1
- package/sidecar/dist/providers/claude/safe-mode.d.ts +5 -5
- package/sidecar/dist/providers/claude/safe-mode.js +49 -30
- package/sidecar/dist/providers/claude/safe-mode.js.map +1 -1
- package/sidecar/dist/providers/claude/secret-files.d.ts +2 -0
- package/sidecar/dist/providers/claude/secret-files.js +50 -0
- package/sidecar/dist/providers/claude/secret-files.js.map +1 -0
- package/sidecar/dist/providers/claude/session-stream.d.ts +4 -9
- package/sidecar/dist/providers/claude/session-stream.js +22 -0
- package/sidecar/dist/providers/claude/session-stream.js.map +1 -1
- package/sidecar/dist/providers/claude/tool-guard.d.ts +14 -0
- package/sidecar/dist/providers/claude/tool-guard.js +112 -0
- package/sidecar/dist/providers/claude/tool-guard.js.map +1 -0
- package/sidecar/dist/providers/codex/adapter.d.ts +10 -0
- package/sidecar/dist/providers/codex/adapter.js +20 -5
- package/sidecar/dist/providers/codex/adapter.js.map +1 -1
- package/sidecar/dist/providers/codex/config.d.ts +6 -17
- package/sidecar/dist/providers/codex/config.js +9 -56
- package/sidecar/dist/providers/codex/config.js.map +1 -1
- package/sidecar/dist/providers/codex/mcp-elicitation.d.ts +21 -0
- package/sidecar/dist/providers/codex/mcp-elicitation.js +66 -0
- package/sidecar/dist/providers/codex/mcp-elicitation.js.map +1 -0
- package/sidecar/dist/providers/codex/module.js +1 -1
- package/sidecar/dist/providers/codex/module.js.map +1 -1
- package/sidecar/dist/providers/codex/patch-directories.d.ts +6 -0
- package/sidecar/dist/providers/codex/patch-directories.js +53 -0
- package/sidecar/dist/providers/codex/patch-directories.js.map +1 -0
- package/sidecar/dist/providers/codex/permission-profiles.d.ts +11 -0
- package/sidecar/dist/providers/codex/permission-profiles.js +37 -0
- package/sidecar/dist/providers/codex/permission-profiles.js.map +1 -0
- package/sidecar/dist/providers/codex/permissions.d.ts +13 -4
- package/sidecar/dist/providers/codex/permissions.js +28 -2
- package/sidecar/dist/providers/codex/permissions.js.map +1 -1
- package/sidecar/dist/providers/codex/process.d.ts +1 -0
- package/sidecar/dist/providers/codex/process.js +3 -1
- package/sidecar/dist/providers/codex/process.js.map +1 -1
- package/sidecar/dist/providers/codex/protocol/common.d.ts +16 -4
- package/sidecar/dist/providers/codex/protocol/index.d.ts +2 -2
- package/sidecar/dist/providers/codex/protocol/index.js +1 -1
- package/sidecar/dist/providers/codex/protocol/index.js.map +1 -1
- package/sidecar/dist/providers/codex/protocol/v2.d.ts +23 -32
- package/sidecar/dist/providers/codex/provider.js +13 -12
- package/sidecar/dist/providers/codex/provider.js.map +1 -1
- package/sidecar/dist/providers/codex/secret-file-globs.d.ts +2 -0
- package/sidecar/dist/providers/codex/secret-file-globs.js +45 -0
- package/sidecar/dist/providers/codex/secret-file-globs.js.map +1 -0
- package/sidecar/dist/providers/types.d.ts +3 -1
- package/sidecar/dist/providers/types.js +2 -2
- package/sidecar/dist/providers/types.js.map +1 -1
- package/sidecar/dist/server/http-routes.js +1 -1
- package/sidecar/dist/server/http-routes.js.map +1 -1
- package/sidecar/dist/server/permission-events.d.ts +1 -1
- package/sidecar/dist/server/permission-events.js +5 -8
- package/sidecar/dist/server/permission-events.js.map +1 -1
- package/sidecar/dist/server/routing.js +0 -5
- package/sidecar/dist/server/routing.js.map +1 -1
- package/sidecar/dist/server/tool-calls.d.ts +0 -2
- package/sidecar/dist/server/tool-calls.js +7 -8
- package/sidecar/dist/server/tool-calls.js.map +1 -1
- package/sidecar/dist/server/turns.js +9 -1
- package/sidecar/dist/server/turns.js.map +1 -1
- package/sidecar/dist/server/ws.js +4 -15
- package/sidecar/dist/server/ws.js.map +1 -1
- package/sidecar/dist/skills/skill-usage.js +2 -2
- package/sidecar/dist/skills/skill-usage.js.map +1 -1
- package/sidecar/dist/state/settings-store.d.ts +13 -5
- package/sidecar/dist/state/settings-store.js +33 -8
- package/sidecar/dist/state/settings-store.js.map +1 -1
- package/sidecar/dist/state/settings-types.d.ts +15 -2
- package/sidecar/dist/state/settings-types.js +14 -1
- package/sidecar/dist/state/settings-types.js.map +1 -1
- package/sidecar/dist/state/types.d.ts +7 -6
- package/sidecar/dist/state/types.js.map +1 -1
- package/sidecar/package.json +1 -1
- package/skills/dms-ai/SKILL.md +2 -1
- package/skills/dms-external-database/SKILL.md +5 -2
- package/frontend-vue/app/chat/components/FullAutoBanner.vue +0 -76
- package/frontend-vue/app/chat/components/FullAutoDialog.vue +0 -103
|
@@ -47,16 +47,14 @@ describe("the chat as a component of the dashboard", () => {
|
|
|
47
47
|
]);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
it("
|
|
50
|
+
it("picks its conversation up again when the panel reopens, without announcing that it left", async () => {
|
|
51
51
|
const harness = createHarness();
|
|
52
52
|
const panel = await openPanel(harness);
|
|
53
53
|
await sendMessage(panel, "build me a page");
|
|
54
54
|
harness.session.panel.close();
|
|
55
55
|
await nextTick();
|
|
56
56
|
expect(panel.find(".chat-view").exists()).toBe(false);
|
|
57
|
-
expect(sentOfType(harness, "leave_conversation")).toEqual([
|
|
58
|
-
{ type: "leave_conversation", conversationId: CONVERSATION_ID },
|
|
59
|
-
]);
|
|
57
|
+
expect(sentOfType(harness, "leave_conversation")).toEqual([]);
|
|
60
58
|
harness.session.panel.toggle();
|
|
61
59
|
await flushPromises();
|
|
62
60
|
expect(sentOfType(harness, "hello")).toEqual([
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import type { VueWrapper } from "@vue/test-utils";
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
4
|
+
import { nextTick } from "vue";
|
|
5
|
+
import {
|
|
6
|
+
CONVERSATION_ID,
|
|
7
|
+
createHarness,
|
|
8
|
+
deliver,
|
|
9
|
+
openPanel,
|
|
10
|
+
unmountPanel,
|
|
11
|
+
} from "./support/panel-harness";
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
localStorage.setItem("dms-ai-conversation-id", CONVERSATION_ID);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
unmountPanel();
|
|
19
|
+
localStorage.clear();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const SETTINGS = {
|
|
23
|
+
defaultModes: { claude: "auto", codex: "normal" },
|
|
24
|
+
thinking: "medium",
|
|
25
|
+
generationMode: "safe",
|
|
26
|
+
allowLocalSkills: false,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const PROVIDERS = {
|
|
30
|
+
claude: { available: true },
|
|
31
|
+
codex: { available: true },
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function modePill(panel: VueWrapper) {
|
|
35
|
+
const pills = panel.findAll(".cb-mode-btn");
|
|
36
|
+
return pills[pills.length - 1];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function pickerItems(panel: VueWrapper): string[] {
|
|
40
|
+
const menu = modePill(panel).element.closest(".u-dropdown");
|
|
41
|
+
return Array.from(menu?.querySelectorAll(".u-dropdown-item") ?? []).map(
|
|
42
|
+
(item) => item.textContent?.trim() ?? "",
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function useProvider(
|
|
47
|
+
harness: ReturnType<typeof createHarness>,
|
|
48
|
+
provider: "claude" | "codex",
|
|
49
|
+
): Promise<void> {
|
|
50
|
+
deliver(harness, {
|
|
51
|
+
type: "settings_update",
|
|
52
|
+
settings: { ...SETTINGS, provider },
|
|
53
|
+
providers: PROVIDERS,
|
|
54
|
+
});
|
|
55
|
+
await nextTick();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
describe("the approval mode pill", () => {
|
|
59
|
+
it("shows Auto review on a fresh Claude chat, without danger styling", async () => {
|
|
60
|
+
const harness = createHarness();
|
|
61
|
+
const panel = await openPanel(harness);
|
|
62
|
+
await useProvider(harness, "claude");
|
|
63
|
+
const pill = modePill(panel);
|
|
64
|
+
expect(pill.text()).toBe("Auto review");
|
|
65
|
+
expect(pickerItems(panel)).toContain("Auto review");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("reads Ask first on Codex for a chat holding auto, and offers no Auto review", async () => {
|
|
69
|
+
const harness = createHarness();
|
|
70
|
+
const panel = await openPanel(harness);
|
|
71
|
+
await useProvider(harness, "codex");
|
|
72
|
+
deliver(harness, {
|
|
73
|
+
type: "conversation_mode",
|
|
74
|
+
mode: "auto",
|
|
75
|
+
generationMode: "safe",
|
|
76
|
+
});
|
|
77
|
+
await nextTick();
|
|
78
|
+
expect(modePill(panel).text()).toBe("Ask first");
|
|
79
|
+
expect(pickerItems(panel)).not.toContain("Auto review");
|
|
80
|
+
expect(pickerItems(panel)).toContain("Ask first");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it.each(["claude", "codex"] as const)(
|
|
84
|
+
"offers no Full auto in the %s mode menu",
|
|
85
|
+
async (provider) => {
|
|
86
|
+
const harness = createHarness();
|
|
87
|
+
const panel = await openPanel(harness);
|
|
88
|
+
await useProvider(harness, provider);
|
|
89
|
+
const items = pickerItems(panel);
|
|
90
|
+
expect(items.length).toBeGreaterThan(0);
|
|
91
|
+
expect(items.filter((item) => /full auto/i.test(item))).toEqual([]);
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
it.each([
|
|
96
|
+
["codex", true],
|
|
97
|
+
["claude", false],
|
|
98
|
+
] as const)(
|
|
99
|
+
"says Accept edits works as Ask first on %s: %s",
|
|
100
|
+
async (provider, worksAsAskFirst) => {
|
|
101
|
+
const harness = createHarness();
|
|
102
|
+
const panel = await openPanel(harness);
|
|
103
|
+
await useProvider(harness, provider);
|
|
104
|
+
const acceptEdits = panel
|
|
105
|
+
.findAll(".u-dropdown-item")
|
|
106
|
+
.find((item) => item.text() === "Accept edits");
|
|
107
|
+
const description = acceptEdits?.attributes("data-description") ?? "";
|
|
108
|
+
expect(description).not.toBe("");
|
|
109
|
+
expect(description.includes("works as Ask first")).toBe(worksAsAskFirst);
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
it("keeps the pill on the chat's own mode when an older sidecar still sends Full auto", async () => {
|
|
114
|
+
const harness = createHarness();
|
|
115
|
+
const panel = await openPanel(harness);
|
|
116
|
+
await useProvider(harness, "claude");
|
|
117
|
+
deliver(harness, {
|
|
118
|
+
type: "conversation_mode",
|
|
119
|
+
mode: "auto",
|
|
120
|
+
generationMode: "safe",
|
|
121
|
+
fullAuto: { duration: "chat" },
|
|
122
|
+
});
|
|
123
|
+
await nextTick();
|
|
124
|
+
expect(modePill(panel).text()).toBe("Auto review");
|
|
125
|
+
expect(panel.find(".cb-banner-auto").exists()).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("says Auto review is not available, naming the model", async () => {
|
|
129
|
+
const harness = createHarness();
|
|
130
|
+
const panel = await openPanel(harness);
|
|
131
|
+
deliver(harness, {
|
|
132
|
+
type: "notice",
|
|
133
|
+
notice: {
|
|
134
|
+
kind: "auto_unavailable",
|
|
135
|
+
model: "claude-haiku-4-5",
|
|
136
|
+
timestampMs: Date.now(),
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
await nextTick();
|
|
140
|
+
const notice = panel.find(".cb-notice");
|
|
141
|
+
expect(notice.text()).toContain("Auto review isn't available here.");
|
|
142
|
+
expect(notice.text()).toContain(
|
|
143
|
+
"claude-haiku-4-5, this plan or a setting doesn't allow it.",
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -106,35 +106,17 @@ describe("questions, queue and modes", () => {
|
|
|
106
106
|
).toEqual(["queue_update", "queue_move", "queue_clear"]);
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
-
it("
|
|
109
|
+
it("sends the chosen mode alone, with nothing that approves for the person", async () => {
|
|
110
110
|
const harness = createHarness();
|
|
111
111
|
const panel = await openPanel(harness);
|
|
112
112
|
await buttonWithText(panel, "Plan only").trigger("click");
|
|
113
|
-
await buttonWithText(panel, "Full auto…").trigger("click");
|
|
114
|
-
await nextTick();
|
|
115
|
-
const modal = panel.find(".u-modal");
|
|
116
|
-
expect(modal.text()).toContain("Turn on Full auto?");
|
|
117
|
-
await buttonWithText(modal, "30 minutes").trigger("click");
|
|
118
|
-
await buttonWithText(modal, "Turn on Full auto").trigger("click");
|
|
119
113
|
expect(sentOfType(harness, "set_conversation_mode")).toEqual([
|
|
120
114
|
{
|
|
121
115
|
type: "set_conversation_mode",
|
|
122
116
|
conversationId: CONVERSATION_ID,
|
|
123
117
|
mode: "plan",
|
|
124
118
|
},
|
|
125
|
-
{
|
|
126
|
-
type: "set_conversation_mode",
|
|
127
|
-
conversationId: CONVERSATION_ID,
|
|
128
|
-
fullAuto: { duration: "30m" },
|
|
129
|
-
},
|
|
130
119
|
]);
|
|
131
|
-
await nextTick();
|
|
132
|
-
const banner = panel.find(".cb-banner-auto");
|
|
133
|
-
expect(banner.text()).toContain("Full auto · for 30 minutes");
|
|
134
|
-
await buttonWithText(banner, "Turn off").trigger("click");
|
|
135
|
-
expect(sentOfType(harness, "set_conversation_mode").at(-1)).toMatchObject({
|
|
136
|
-
fullAuto: null,
|
|
137
|
-
});
|
|
138
120
|
});
|
|
139
121
|
|
|
140
122
|
it("runs a proposed plan in Ask first", async () => {
|
|
@@ -60,16 +60,6 @@ describe("the panel's header and empty chat", () => {
|
|
|
60
60
|
);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
it("sends leave_conversation when the panel closes", async () => {
|
|
64
|
-
const harness = createHarness();
|
|
65
|
-
await openPanel(harness);
|
|
66
|
-
harness.session.panel.close();
|
|
67
|
-
await nextTick();
|
|
68
|
-
expect(sentOfType(harness, "leave_conversation")).toEqual([
|
|
69
|
-
{ type: "leave_conversation", conversationId: CONVERSATION_ID },
|
|
70
|
-
]);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
63
|
it("stamps a message with the page unless its chip was removed", async () => {
|
|
74
64
|
const harness = createHarness();
|
|
75
65
|
const panel = await openPanel(harness);
|
|
@@ -116,7 +106,7 @@ describe("notices and errors", () => {
|
|
|
116
106
|
type: "settings_update",
|
|
117
107
|
settings: {
|
|
118
108
|
provider: "codex",
|
|
119
|
-
|
|
109
|
+
defaultModes: { claude: "auto", codex: "normal" },
|
|
120
110
|
thinking: "medium",
|
|
121
111
|
generationMode: "safe",
|
|
122
112
|
allowLocalSkills: false,
|
|
@@ -161,7 +151,7 @@ describe("the history drawer", () => {
|
|
|
161
151
|
id: "c-old",
|
|
162
152
|
title: "Why is order pending?",
|
|
163
153
|
createdAtMs: 1,
|
|
164
|
-
updatedAtMs: Date.now() -
|
|
154
|
+
updatedAtMs: Date.now() - 1_000,
|
|
165
155
|
messageCount: 2,
|
|
166
156
|
filesChanged: 0,
|
|
167
157
|
filesUndone: 1,
|
|
@@ -185,9 +175,7 @@ describe("the history drawer", () => {
|
|
|
185
175
|
expect(drawer.text()).toContain("9.6k tok");
|
|
186
176
|
expect(drawer.text()).toContain("1 file undone");
|
|
187
177
|
await buttonWithText(drawer, "Why is order pending?").trigger("click");
|
|
188
|
-
expect(sentOfType(harness, "leave_conversation")[
|
|
189
|
-
conversationId: CONVERSATION_ID,
|
|
190
|
-
});
|
|
178
|
+
expect(sentOfType(harness, "leave_conversation")).toEqual([]);
|
|
191
179
|
expect(sentOfType(harness, "hello").at(-1)).toMatchObject({
|
|
192
180
|
conversationId: "c-old",
|
|
193
181
|
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
type ResizeCallback = () => void;
|
|
2
|
+
|
|
3
|
+
const observers = new Set<FakeResizeObserver>();
|
|
4
|
+
const pendingScrollEvents = new Set<() => void>();
|
|
5
|
+
const layoutPasses = new Set<() => void>();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* jsdom has no layout, so no resize ever happens by itself: a test reports
|
|
9
|
+
* one with `settleLayout`, as the browser does once per frame.
|
|
10
|
+
*/
|
|
11
|
+
class FakeResizeObserver {
|
|
12
|
+
private readonly targets = new Set<Element>();
|
|
13
|
+
|
|
14
|
+
constructor(private readonly callback: ResizeCallback) {
|
|
15
|
+
observers.add(this);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
observe(target: Element): void {
|
|
19
|
+
this.targets.add(target);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
unobserve(target: Element): void {
|
|
23
|
+
this.targets.delete(target);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
disconnect(): void {
|
|
27
|
+
this.targets.clear();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
deliver(): void {
|
|
31
|
+
if (this.targets.size > 0) this.callback();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function installResizeObserver(): void {
|
|
36
|
+
Reflect.set(globalThis, "ResizeObserver", FakeResizeObserver);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function dispatchPendingScrollEvents(): void {
|
|
40
|
+
for (const dispatch of [...pendingScrollEvents]) dispatch();
|
|
41
|
+
pendingScrollEvents.clear();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A frame: scroll events fire first, then layout runs and the browser clamps
|
|
46
|
+
* or anchors the scroll position, then resizes are reported, and the scroll
|
|
47
|
+
* events layout caused fire at the start of the next frame.
|
|
48
|
+
*/
|
|
49
|
+
export function settleLayout(): void {
|
|
50
|
+
dispatchPendingScrollEvents();
|
|
51
|
+
for (const pass of layoutPasses) pass();
|
|
52
|
+
for (const observer of [...observers]) observer.deliver();
|
|
53
|
+
dispatchPendingScrollEvents();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface FakeScroller {
|
|
57
|
+
/** Height of what the list draws, beside one row height per message. */
|
|
58
|
+
extraContentPx: number;
|
|
59
|
+
viewportPx: number;
|
|
60
|
+
distanceFromBottom: () => number;
|
|
61
|
+
/** A scroll to `scrollTop` and the event it fires, from any source. */
|
|
62
|
+
scrollTo: (scrollTop: number) => void;
|
|
63
|
+
/**
|
|
64
|
+
* Content above the viewport shrinks by `px` at the next layout, and scroll
|
|
65
|
+
* anchoring moves the scroll position up by as much to keep the view still.
|
|
66
|
+
*/
|
|
67
|
+
collapseAbove: (px: number) => void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const ROW_PX = 100;
|
|
71
|
+
|
|
72
|
+
/** Gives a scroll container the geometry a browser would compute. */
|
|
73
|
+
export function fakeScroller(el: HTMLElement): FakeScroller {
|
|
74
|
+
let scrollTop = 0;
|
|
75
|
+
let reportedTop = 0;
|
|
76
|
+
let anchorShiftPx = 0;
|
|
77
|
+
const dispatchScroll = (): void => {
|
|
78
|
+
el.dispatchEvent(new Event("scroll"));
|
|
79
|
+
};
|
|
80
|
+
const scroller: FakeScroller = {
|
|
81
|
+
extraContentPx: 0,
|
|
82
|
+
viewportPx: 400,
|
|
83
|
+
distanceFromBottom: () => el.scrollHeight - el.scrollTop - el.clientHeight,
|
|
84
|
+
scrollTo: (target) => {
|
|
85
|
+
dispatchPendingScrollEvents();
|
|
86
|
+
el.scrollTop = target;
|
|
87
|
+
dispatchPendingScrollEvents();
|
|
88
|
+
},
|
|
89
|
+
collapseAbove: (px) => {
|
|
90
|
+
scroller.extraContentPx -= px;
|
|
91
|
+
anchorShiftPx -= px;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
const maxTop = (): number =>
|
|
95
|
+
Math.max(0, el.scrollHeight - scroller.viewportPx);
|
|
96
|
+
layoutPasses.add(() => {
|
|
97
|
+
scrollTop = Math.min(Math.max(0, el.scrollTop + anchorShiftPx), maxTop());
|
|
98
|
+
anchorShiftPx = 0;
|
|
99
|
+
if (scrollTop === reportedTop) return;
|
|
100
|
+
reportedTop = scrollTop;
|
|
101
|
+
pendingScrollEvents.add(dispatchScroll);
|
|
102
|
+
});
|
|
103
|
+
Object.defineProperties(el, {
|
|
104
|
+
clientHeight: { get: () => scroller.viewportPx },
|
|
105
|
+
scrollHeight: {
|
|
106
|
+
get: () =>
|
|
107
|
+
el.querySelectorAll("li").length * ROW_PX + scroller.extraContentPx,
|
|
108
|
+
},
|
|
109
|
+
scrollTop: {
|
|
110
|
+
get: () => Math.min(scrollTop, maxTop()),
|
|
111
|
+
set: (value: number) => {
|
|
112
|
+
scrollTop = Math.min(Math.max(0, value), maxTop());
|
|
113
|
+
reportedTop = scrollTop;
|
|
114
|
+
pendingScrollEvents.add(dispatchScroll);
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
return scroller;
|
|
119
|
+
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { flushPromises, mount, type VueWrapper } from "@vue/test-utils";
|
|
2
|
-
import { vi } from "vitest";
|
|
3
|
-
import {
|
|
2
|
+
import { type Mock, vi } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
defineComponent,
|
|
5
|
+
h,
|
|
6
|
+
nextTick,
|
|
7
|
+
readonly,
|
|
8
|
+
ref,
|
|
9
|
+
type Ref,
|
|
10
|
+
shallowRef,
|
|
11
|
+
} from "vue";
|
|
4
12
|
import ChatPanel from "../../app/components/ChatPanel.vue";
|
|
5
13
|
import {
|
|
6
14
|
ASSISTANT_SESSION_KEY,
|
|
@@ -26,11 +34,11 @@ export interface Harness {
|
|
|
26
34
|
hub: ChatTransportHub;
|
|
27
35
|
sent: Array<Record<string, unknown>>;
|
|
28
36
|
reconnects: number;
|
|
29
|
-
sidecarStatus:
|
|
37
|
+
sidecarStatus: Ref<SidecarStatus>;
|
|
30
38
|
setChannelStatus: (status: ChannelStatus) => void;
|
|
31
|
-
navigate:
|
|
32
|
-
restart:
|
|
33
|
-
lastError:
|
|
39
|
+
navigate: Mock<(path: string) => void>;
|
|
40
|
+
restart: Mock<() => Promise<void>>;
|
|
41
|
+
lastError: Ref<string | null>;
|
|
34
42
|
api: AssistantSession["api"];
|
|
35
43
|
/** How many holds keep the tab's stream open. */
|
|
36
44
|
streamHolds: number;
|
|
@@ -64,6 +64,7 @@ const UModal = defineComponent({
|
|
|
64
64
|
|
|
65
65
|
interface MenuItem {
|
|
66
66
|
label?: string;
|
|
67
|
+
description?: string;
|
|
67
68
|
onSelect?: () => void;
|
|
68
69
|
}
|
|
69
70
|
|
|
@@ -84,6 +85,7 @@ const UDropdownMenu = defineComponent({
|
|
|
84
85
|
{
|
|
85
86
|
class: "u-dropdown-item",
|
|
86
87
|
type: "button",
|
|
88
|
+
"data-description": item.description,
|
|
87
89
|
onClick: item.onSelect,
|
|
88
90
|
},
|
|
89
91
|
item.label,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/dms-ai",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "AI development assistant for the AntelopeJS DMS: a chat panel next to every page and a coding agent that edits the project in reviewable, undoable change sets.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"suppressions:update": "antelopejs-suppressions --update"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
+
"@antelopejs/agent-skills": "^0.1.0",
|
|
79
80
|
"@antelopejs/interface-api": ">=0.0.15 <1.0.0",
|
|
80
81
|
"@antelopejs/interface-core": ">=0.2.4 <1.0.0",
|
|
81
82
|
"@antelopejs/interface-dms": ">=0.9.0 <1.0.0",
|
|
@@ -101,7 +102,7 @@
|
|
|
101
102
|
"typescript": "^6.0.3"
|
|
102
103
|
},
|
|
103
104
|
"peerDependencies": {
|
|
104
|
-
"@openai/codex": "0.
|
|
105
|
+
"@openai/codex": "0.166.0"
|
|
105
106
|
},
|
|
106
107
|
"peerDependenciesMeta": {
|
|
107
108
|
"@openai/codex": {
|
|
@@ -110,7 +111,7 @@
|
|
|
110
111
|
},
|
|
111
112
|
"optionalDependencies": {
|
|
112
113
|
"@antelopejs/interface-database-catalog": ">=0.0.1 <1.0.0",
|
|
113
|
-
"@antelopejs/interface-dms-builder": ">=0.3.
|
|
114
|
+
"@antelopejs/interface-dms-builder": ">=0.3.1 <1.0.0"
|
|
114
115
|
},
|
|
115
116
|
"packageManager": "pnpm@10.2.0",
|
|
116
117
|
"antelopeJs": {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
import { DEPENDENCY_FILE_NAMES, GIT_HOOK_PATHS, } from "../constants/permissions.js";
|
|
3
|
+
const ANY_TRAILING_SEGMENTS = "**";
|
|
4
|
+
const GIT_HOOK_PATTERNS = GIT_HOOK_PATHS.map((path) => path.split("/"));
|
|
5
|
+
function pathSegments(path) {
|
|
6
|
+
return path
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.split(/[\\/]/)
|
|
9
|
+
.filter((segment) => segment.length > 0);
|
|
10
|
+
}
|
|
11
|
+
function matchesAt(segments, parts, start) {
|
|
12
|
+
return parts.every((part, index) => segments[start + index] === part);
|
|
13
|
+
}
|
|
14
|
+
function isUnder(segments, parts) {
|
|
15
|
+
return segments.some((_segment, start) => start + parts.length < segments.length &&
|
|
16
|
+
matchesAt(segments, parts, start));
|
|
17
|
+
}
|
|
18
|
+
function endsWith(segments, parts) {
|
|
19
|
+
const start = segments.length - parts.length;
|
|
20
|
+
return start >= 0 && matchesAt(segments, parts, start);
|
|
21
|
+
}
|
|
22
|
+
function matchesPathEnding(segments, pattern) {
|
|
23
|
+
return pattern.at(-1) === ANY_TRAILING_SEGMENTS
|
|
24
|
+
? isUnder(segments, pattern.slice(0, -1))
|
|
25
|
+
: endsWith(segments, pattern);
|
|
26
|
+
}
|
|
27
|
+
export function isGitHookFile(path) {
|
|
28
|
+
const segments = pathSegments(path);
|
|
29
|
+
return GIT_HOOK_PATTERNS.some((pattern) => matchesPathEnding(segments, pattern));
|
|
30
|
+
}
|
|
31
|
+
export function isDependencyFile(path) {
|
|
32
|
+
return DEPENDENCY_FILE_NAMES.has(basename(path).toLowerCase());
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=always-ask-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"always-ask-files.js","sourceRoot":"","sources":["../../src/agent/always-ask-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EACL,qBAAqB,EACrB,cAAc,GACf,MAAM,6BAA6B,CAAC;AAErC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAEnC,MAAM,iBAAiB,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AAExE,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,IAAI;SACR,WAAW,EAAE;SACb,KAAK,CAAC,OAAO,CAAC;SACd,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,SAAS,SAAS,CAChB,QAA2B,EAC3B,KAAwB,EACxB,KAAa;IAEb,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;AACxE,CAAC;AAED,SAAS,OAAO,CACd,QAA2B,EAC3B,KAAwB;IAExB,OAAO,QAAQ,CAAC,IAAI,CAClB,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,CAClB,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM;QACtC,SAAS,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CACpC,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CACf,QAA2B,EAC3B,KAAwB;IAExB,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC7C,OAAO,KAAK,IAAI,CAAC,IAAI,SAAS,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,iBAAiB,CACxB,QAA2B,EAC3B,OAA0B;IAE1B,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,qBAAqB;QAC7C,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACzC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;AAClC,CAAC;AAMD,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CACxC,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,CACrC,CAAC;AACJ,CAAC;AAMD,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,qBAAqB,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AACjE,CAAC"}
|
|
@@ -1,32 +1,23 @@
|
|
|
1
|
-
import type { FullAutoDuration } from "../constants/audit.js";
|
|
2
1
|
import type { ConversationModeType } from "../protocol/events.js";
|
|
3
2
|
import type { ConversationStore } from "../state/conversations.js";
|
|
4
3
|
import type { SettingsStore } from "../state/settings-store.js";
|
|
5
|
-
import type
|
|
4
|
+
import { type AppSettings, type ChatMode, type GenerationMode, type TurnSettings } from "../state/settings-types.js";
|
|
6
5
|
import { type ConversationModeState } from "./effective-mode.js";
|
|
7
6
|
export interface ConversationModesPatch {
|
|
8
7
|
mode?: ChatMode;
|
|
9
8
|
generationMode?: GenerationMode;
|
|
10
|
-
fullAuto?: FullAutoRequest | null;
|
|
11
|
-
}
|
|
12
|
-
interface FullAutoRequest {
|
|
13
|
-
duration: FullAutoDuration;
|
|
14
9
|
}
|
|
15
10
|
export interface ConversationModesDeps {
|
|
16
11
|
conversationStore: ConversationStore;
|
|
17
12
|
settingsStore: SettingsStore;
|
|
18
|
-
onChanged: (conversationId: string
|
|
13
|
+
onChanged: (conversationId: string) => void;
|
|
19
14
|
}
|
|
20
15
|
export interface ConversationModes {
|
|
21
16
|
state(conversationId: string): ConversationModeState;
|
|
22
17
|
describe(conversationId: string): ConversationModeType;
|
|
23
18
|
update(conversationId: string, patch: ConversationModesPatch): void;
|
|
24
19
|
pinDefaults(conversationId: string): void;
|
|
25
|
-
settingsFor(conversationId: string, settings: AppSettings):
|
|
26
|
-
isFullAuto(conversationId: string): boolean;
|
|
27
|
-
endTurn(conversationId: string): void;
|
|
28
|
-
leave(conversationId: string): void;
|
|
20
|
+
settingsFor(conversationId: string, settings: AppSettings): TurnSettings;
|
|
29
21
|
forget(conversationId: string): void;
|
|
30
22
|
}
|
|
31
23
|
export declare function createConversationModes(deps: ConversationModesDeps): ConversationModes;
|
|
32
|
-
export {};
|
|
@@ -1,61 +1,24 @@
|
|
|
1
1
|
import { effectiveGenerationMode } from "../builder/capability.js";
|
|
2
|
-
import {
|
|
3
|
-
import { conversationSettings,
|
|
4
|
-
|
|
5
|
-
const FULL_AUTO_CLOCK_MS = {
|
|
6
|
-
"30m": FULL_AUTO_TIMED_MINUTES * MS_PER_MINUTE,
|
|
7
|
-
};
|
|
8
|
-
function readState(state, conversationId) {
|
|
2
|
+
import { modeForProvider, } from "../state/settings-types.js";
|
|
3
|
+
import { conversationSettings, } from "./effective-mode.js";
|
|
4
|
+
function readChosen(state, conversationId) {
|
|
9
5
|
const stored = state.conversationStore.get(conversationId);
|
|
10
6
|
const draft = state.drafts.get(conversationId);
|
|
11
7
|
const defaults = state.settingsStore.get();
|
|
12
8
|
return {
|
|
13
|
-
mode: stored?.mode ?? draft?.mode ?? defaults.
|
|
9
|
+
mode: stored?.mode ?? draft?.mode ?? defaults.defaultModes[defaults.provider],
|
|
14
10
|
generationMode: stored?.generationMode ??
|
|
15
11
|
draft?.generationMode ??
|
|
16
12
|
defaults.generationMode,
|
|
17
|
-
fullAuto: state.fullAuto.get(conversationId)?.state ?? null,
|
|
18
13
|
};
|
|
19
14
|
}
|
|
20
|
-
function
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
function endFullAuto(state, conversationId, duration) {
|
|
29
|
-
const entry = state.fullAuto.get(conversationId);
|
|
30
|
-
if (entry?.state.duration !== duration)
|
|
31
|
-
return;
|
|
32
|
-
clearFullAuto(state, conversationId);
|
|
33
|
-
state.onChanged(conversationId, true);
|
|
34
|
-
}
|
|
35
|
-
function startFullAuto(state, conversationId, request) {
|
|
36
|
-
clearFullAuto(state, conversationId);
|
|
37
|
-
const clockMs = FULL_AUTO_CLOCK_MS[request.duration];
|
|
38
|
-
if (clockMs === undefined) {
|
|
39
|
-
state.fullAuto.set(conversationId, {
|
|
40
|
-
state: { duration: request.duration },
|
|
41
|
-
});
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
const timer = setTimeout(() => endFullAuto(state, conversationId, request.duration), clockMs);
|
|
45
|
-
timer.unref();
|
|
46
|
-
state.fullAuto.set(conversationId, {
|
|
47
|
-
state: { duration: request.duration, untilMs: Date.now() + clockMs },
|
|
48
|
-
timer,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
function applyFullAuto(state, conversationId, request) {
|
|
52
|
-
if (request === undefined)
|
|
53
|
-
return;
|
|
54
|
-
if (request === null) {
|
|
55
|
-
clearFullAuto(state, conversationId);
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
startFullAuto(state, conversationId, request);
|
|
15
|
+
function readState(state, conversationId) {
|
|
16
|
+
const chosen = readChosen(state, conversationId);
|
|
17
|
+
const { provider } = state.settingsStore.get();
|
|
18
|
+
return {
|
|
19
|
+
mode: modeForProvider(provider, chosen.mode),
|
|
20
|
+
generationMode: chosen.generationMode,
|
|
21
|
+
};
|
|
59
22
|
}
|
|
60
23
|
function storeModes(state, conversationId, patch) {
|
|
61
24
|
if (state.conversationStore.get(conversationId) === null) {
|
|
@@ -70,12 +33,8 @@ function storeModes(state, conversationId, patch) {
|
|
|
70
33
|
}
|
|
71
34
|
function update(state, conversationId, patch) {
|
|
72
35
|
pinDefaults(state, conversationId);
|
|
73
|
-
storeModes(state, conversationId,
|
|
74
|
-
|
|
75
|
-
generationMode: patch.generationMode,
|
|
76
|
-
});
|
|
77
|
-
applyFullAuto(state, conversationId, patch.fullAuto);
|
|
78
|
-
state.onChanged(conversationId, false);
|
|
36
|
+
storeModes(state, conversationId, patch);
|
|
37
|
+
state.onChanged(conversationId);
|
|
79
38
|
}
|
|
80
39
|
function pinDefaults(state, conversationId) {
|
|
81
40
|
const stored = state.conversationStore.get(conversationId);
|
|
@@ -83,19 +42,12 @@ function pinDefaults(state, conversationId) {
|
|
|
83
42
|
return;
|
|
84
43
|
if (stored.mode !== undefined && stored.generationMode !== undefined)
|
|
85
44
|
return;
|
|
86
|
-
const
|
|
45
|
+
const chosen = readChosen(state, conversationId);
|
|
87
46
|
state.drafts.delete(conversationId);
|
|
88
|
-
state.conversationStore.setModes(conversationId,
|
|
89
|
-
mode: current.mode,
|
|
90
|
-
generationMode: current.generationMode,
|
|
91
|
-
});
|
|
47
|
+
state.conversationStore.setModes(conversationId, chosen);
|
|
92
48
|
}
|
|
93
49
|
export function createConversationModes(deps) {
|
|
94
|
-
const state = {
|
|
95
|
-
...deps,
|
|
96
|
-
fullAuto: new Map(),
|
|
97
|
-
drafts: new Map(),
|
|
98
|
-
};
|
|
50
|
+
const state = { ...deps, drafts: new Map() };
|
|
99
51
|
return {
|
|
100
52
|
state: (conversationId) => readState(state, conversationId),
|
|
101
53
|
describe(conversationId) {
|
|
@@ -104,17 +56,12 @@ export function createConversationModes(deps) {
|
|
|
104
56
|
conversationId,
|
|
105
57
|
mode: current.mode,
|
|
106
58
|
generationMode: effectiveGenerationMode(current.generationMode),
|
|
107
|
-
fullAuto: current.fullAuto,
|
|
108
59
|
};
|
|
109
60
|
},
|
|
110
61
|
update: (conversationId, patch) => update(state, conversationId, patch),
|
|
111
62
|
pinDefaults: (conversationId) => pinDefaults(state, conversationId),
|
|
112
63
|
settingsFor: (conversationId, settings) => conversationSettings(settings, readState(state, conversationId)),
|
|
113
|
-
isFullAuto: (conversationId) => isFullAutoInForce(readState(state, conversationId)),
|
|
114
|
-
endTurn: (conversationId) => endFullAuto(state, conversationId, "turn"),
|
|
115
|
-
leave: (conversationId) => endFullAuto(state, conversationId, "chat"),
|
|
116
64
|
forget: (conversationId) => {
|
|
117
|
-
clearFullAuto(state, conversationId);
|
|
118
65
|
state.drafts.delete(conversationId);
|
|
119
66
|
},
|
|
120
67
|
};
|