heyio 1.4.3 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/api/server.js +11 -4
- package/dist/chat/attachments.js +76 -0
- package/dist/copilot/agents.js +37 -3
- package/dist/copilot/ceremonies.js +13 -6
- package/dist/copilot/orchestrator.js +23 -5
- package/dist/copilot/tools.js +26 -4
- package/dist/store/conversations.js +14 -2
- package/dist/store/db.js +7 -0
- package/package.json +1 -1
- package/web-dist/assets/{AuditLogView-blV2zh1J.js → AuditLogView-Ds7LwE01.js} +1 -1
- package/web-dist/assets/ChatView-90GHN5-3.js +11 -0
- package/web-dist/assets/{FeedView-BaGHVS5k.js → FeedView-RlM4m0Dp.js} +1 -1
- package/web-dist/assets/{HistoryView-54tTZBvl.js → HistoryView-Vrb5nk4l.js} +1 -1
- package/web-dist/assets/{LoginView-Bwl96p9a.js → LoginView-BVt0eVoK.js} +1 -1
- package/web-dist/assets/{McpView-D1HjpDaS.js → McpView-DMg1Ieji.js} +1 -1
- package/web-dist/assets/{SchedulesView-heDgZ6EX.js → SchedulesView-CvasMUhn.js} +1 -1
- package/web-dist/assets/{SettingsView-BkzrjzlA.js → SettingsView-DqNi_5IM.js} +1 -1
- package/web-dist/assets/{SkillsView-BFEE6Dr7.js → SkillsView-CufZGrhf.js} +1 -1
- package/web-dist/assets/{SquadDetailView-B8eH9uCp.js → SquadDetailView-DmseEdEl.js} +1 -1
- package/web-dist/assets/{SquadHealthView-S7L-ux5R.js → SquadHealthView-D4kmk8OD.js} +1 -1
- package/web-dist/assets/{SquadsView-CYHJHUTi.js → SquadsView-CVB2pCwd.js} +1 -1
- package/web-dist/assets/{UsageView-Brck8R6H.js → UsageView-xEe1ZUp4.js} +1 -1
- package/web-dist/assets/{WikiView-CXXw3RGo.js → WikiView-ADnv-80y.js} +7 -12
- package/web-dist/assets/{api-C7MAaSLt.js → api-2XP2yG1A.js} +1 -1
- package/web-dist/assets/{arrow-left-4L8HoaXC.js → arrow-left-BsbWSKAE.js} +1 -1
- package/web-dist/assets/file-text-Dn2yDZCL.js +6 -0
- package/web-dist/assets/{git-branch-CmEyTvTW.js → git-branch-wkQUgAGS.js} +1 -1
- package/web-dist/assets/{index-CQ_szaoT.css → index-Cd9kiyp4.css} +1 -1
- package/web-dist/assets/{index-1mPHTPeR.js → index-DI8QgDXv.js} +35 -35
- package/web-dist/assets/{plus-BhwZCs3M.js → plus-BOXwfDpA.js} +1 -1
- package/web-dist/assets/{save-Cl5b973n.js → save-CTfFmP-U.js} +1 -1
- package/web-dist/assets/{search-BeUz6qvn.js → search-BnsxENnH.js} +1 -1
- package/web-dist/assets/{trash-2-CQowMVCO.js → trash-2-C86P23n5.js} +1 -1
- package/web-dist/assets/{triangle-alert-zxlYFyjw.js → triangle-alert-MLnkHsRL.js} +1 -1
- package/web-dist/assets/{x-Czw2unv0.js → x-Cw7ZjfeL.js} +1 -1
- package/web-dist/index.html +2 -2
- package/web-dist/assets/ChatView-rktzvf02.js +0 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ IO is a personal AI assistant daemon built on the GitHub Copilot SDK. It runs 24
|
|
|
11
11
|
- **Copilot SDK Integration** — powered by GitHub's Copilot SDK for LLM conversations with tool calling
|
|
12
12
|
- **Multi-Interface** — Web dashboard + Telegram bot + HTTP API
|
|
13
13
|
- **Web Frontend** — Vue 3 dashboard with chat, squad management, skills, and agent activity views
|
|
14
|
+
- **Web Chat Attachments** — Attach images/files in web chat (10MB per file, 25MB per message) with multimodal handoff
|
|
14
15
|
- **Persistent Memory** — wiki-based knowledge base stored at `~/.io/wiki/`
|
|
15
16
|
- **Squad System** — persistent project teams with **named specialist agents** themed from pop culture universes (dynamically selected at squad creation)
|
|
16
17
|
- **Skills** — modular skill system; install from git repos or the [skills.sh](https://skills.sh) registry
|
package/dist/api/server.js
CHANGED
|
@@ -22,6 +22,7 @@ import { saveMessage, getConversation, listConversations, searchConversations, d
|
|
|
22
22
|
import { getTokenUsageSummary, getTokenUsageBySquad, getTokenUsageByAgent, getDailyTokenUsage, } from "../store/token-usage.js";
|
|
23
23
|
import { DEFAULT_MODEL_PRICING } from "../copilot/token-tracker.js";
|
|
24
24
|
import { randomUUID } from "node:crypto";
|
|
25
|
+
import { validateMessageAttachments } from "../chat/attachments.js";
|
|
25
26
|
const __filename = fileURLToPath(import.meta.url);
|
|
26
27
|
const __dirname = dirname(__filename);
|
|
27
28
|
const sseClients = [];
|
|
@@ -33,7 +34,7 @@ function broadcast(event, data) {
|
|
|
33
34
|
}
|
|
34
35
|
export async function startApiServer(config) {
|
|
35
36
|
const app = express();
|
|
36
|
-
app.use(express.json());
|
|
37
|
+
app.use(express.json({ limit: "30mb" }));
|
|
37
38
|
// Serve static web frontend
|
|
38
39
|
const webDistPath = resolve(__dirname, "..", "..", "web-dist");
|
|
39
40
|
app.use(express.static(webDistPath));
|
|
@@ -64,14 +65,20 @@ export async function startApiServer(config) {
|
|
|
64
65
|
});
|
|
65
66
|
// --- Chat (SSE-streamed response) ---
|
|
66
67
|
app.post("/api/message", async (req, res) => {
|
|
67
|
-
const { prompt, conversationId: clientConvId } = req.body;
|
|
68
|
+
const { prompt, conversationId: clientConvId, attachments: rawAttachments } = req.body;
|
|
68
69
|
if (!prompt || typeof prompt !== "string") {
|
|
69
70
|
res.status(400).json({ error: "prompt is required" });
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
73
|
+
const validation = validateMessageAttachments(rawAttachments);
|
|
74
|
+
if (!validation.ok) {
|
|
75
|
+
res.status(400).json({ error: validation.error });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const attachments = validation.attachments;
|
|
72
79
|
const conversationId = (typeof clientConvId === "string" && clientConvId) ? clientConvId : randomUUID();
|
|
73
80
|
// Persist the user message
|
|
74
|
-
saveMessage(conversationId, "user", prompt, "web");
|
|
81
|
+
saveMessage(conversationId, "user", prompt, "web", attachments);
|
|
75
82
|
// Switch to SSE streaming to avoid Cloudflare 524 timeouts
|
|
76
83
|
res.setHeader("Content-Type", "text/event-stream");
|
|
77
84
|
res.setHeader("Cache-Control", "no-cache");
|
|
@@ -99,7 +106,7 @@ export async function startApiServer(config) {
|
|
|
99
106
|
// Also broadcast to other SSE listeners (e.g. ChatOverlay)
|
|
100
107
|
broadcast("message_delta", { content, done: false });
|
|
101
108
|
}
|
|
102
|
-
});
|
|
109
|
+
}, attachments);
|
|
103
110
|
}
|
|
104
111
|
catch (err) {
|
|
105
112
|
if (!closed) {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const MAX_ATTACHMENT_BYTES = 10 * 1024 * 1024;
|
|
2
|
+
export const MAX_TOTAL_ATTACHMENT_BYTES = 25 * 1024 * 1024;
|
|
3
|
+
const BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
4
|
+
function isValidBase64(value) {
|
|
5
|
+
if (!value || value.length % 4 !== 0)
|
|
6
|
+
return false;
|
|
7
|
+
return BASE64_RE.test(value);
|
|
8
|
+
}
|
|
9
|
+
function isMessageAttachment(value) {
|
|
10
|
+
if (!value || typeof value !== "object")
|
|
11
|
+
return false;
|
|
12
|
+
const candidate = value;
|
|
13
|
+
return (typeof candidate.name === "string" &&
|
|
14
|
+
candidate.name.trim().length > 0 &&
|
|
15
|
+
typeof candidate.mimeType === "string" &&
|
|
16
|
+
candidate.mimeType.trim().length > 0 &&
|
|
17
|
+
typeof candidate.size === "number" &&
|
|
18
|
+
Number.isFinite(candidate.size) &&
|
|
19
|
+
candidate.size >= 0 &&
|
|
20
|
+
typeof candidate.content === "string");
|
|
21
|
+
}
|
|
22
|
+
export function validateMessageAttachments(input) {
|
|
23
|
+
if (input === undefined || input === null) {
|
|
24
|
+
return { ok: true, attachments: [] };
|
|
25
|
+
}
|
|
26
|
+
if (!Array.isArray(input)) {
|
|
27
|
+
return { ok: false, error: "attachments must be an array" };
|
|
28
|
+
}
|
|
29
|
+
let totalSize = 0;
|
|
30
|
+
const attachments = [];
|
|
31
|
+
for (const raw of input) {
|
|
32
|
+
if (!isMessageAttachment(raw)) {
|
|
33
|
+
return {
|
|
34
|
+
ok: false,
|
|
35
|
+
error: "each attachment must include name, mimeType, size, and content",
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (raw.size > MAX_ATTACHMENT_BYTES) {
|
|
39
|
+
return { ok: false, error: "each attachment must be 10MB or smaller" };
|
|
40
|
+
}
|
|
41
|
+
if (!isValidBase64(raw.content)) {
|
|
42
|
+
return { ok: false, error: `attachment "${raw.name}" has invalid base64 content` };
|
|
43
|
+
}
|
|
44
|
+
const trimmedName = raw.name.trim();
|
|
45
|
+
const trimmedMimeType = raw.mimeType.trim();
|
|
46
|
+
if (!trimmedName || !trimmedMimeType) {
|
|
47
|
+
return { ok: false, error: "attachment name and mimeType are required" };
|
|
48
|
+
}
|
|
49
|
+
totalSize += raw.size;
|
|
50
|
+
attachments.push({
|
|
51
|
+
name: trimmedName,
|
|
52
|
+
mimeType: trimmedMimeType,
|
|
53
|
+
size: raw.size,
|
|
54
|
+
content: raw.content,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (totalSize > MAX_TOTAL_ATTACHMENT_BYTES) {
|
|
58
|
+
return { ok: false, error: "total attachment size must be 25MB or smaller" };
|
|
59
|
+
}
|
|
60
|
+
return { ok: true, attachments };
|
|
61
|
+
}
|
|
62
|
+
export function toCopilotBlobAttachments(attachments) {
|
|
63
|
+
return attachments.map((attachment) => ({
|
|
64
|
+
type: "blob",
|
|
65
|
+
data: attachment.content,
|
|
66
|
+
mimeType: attachment.mimeType,
|
|
67
|
+
displayName: attachment.name,
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
export function buildAttachmentSummary(attachments) {
|
|
71
|
+
if (attachments.length === 0)
|
|
72
|
+
return "";
|
|
73
|
+
const lines = attachments.map((attachment) => `- ${attachment.name} (${attachment.mimeType}, ${Math.round(attachment.size / 1024)}KB)`);
|
|
74
|
+
return `\n\n[Attachments]\n${lines.join("\n")}`;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=attachments.js.map
|
package/dist/copilot/agents.js
CHANGED
|
@@ -12,6 +12,7 @@ import { PATHS } from "../paths.js";
|
|
|
12
12
|
import { createSquadTools } from "./squad-tools.js";
|
|
13
13
|
import { loadSkillDirectories } from "./skills.js";
|
|
14
14
|
import { getMcpServersForSession } from "../mcp/registry.js";
|
|
15
|
+
import { buildAttachmentSummary, toCopilotBlobAttachments } from "../chat/attachments.js";
|
|
15
16
|
import { existsSync, mkdirSync } from "node:fs";
|
|
16
17
|
import { join } from "node:path";
|
|
17
18
|
import { exec } from "node:child_process";
|
|
@@ -77,7 +78,7 @@ export async function stopTask(taskId) {
|
|
|
77
78
|
updateAgentStatus(task.agent_id, "idle");
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
|
-
export async function delegateTask(squadId, task, instanceId) {
|
|
81
|
+
export async function delegateTask(squadId, task, instanceId, attachments = []) {
|
|
81
82
|
const lead = getLeadForSquad(squadId);
|
|
82
83
|
if (!lead) {
|
|
83
84
|
throw new Error("Squad has no team lead. Add a lead agent first.");
|
|
@@ -102,6 +103,29 @@ export async function delegateTask(squadId, task, instanceId) {
|
|
|
102
103
|
const agentRoster = agents
|
|
103
104
|
.map((a) => `- ${a.character_name} (${a.role_title})${a.is_lead ? " [LEAD]" : ""}${a.is_qa ? " [QA]" : ""}${a.is_test ? " [TEST]" : ""}`)
|
|
104
105
|
.join("\n");
|
|
106
|
+
// Load squad wiki pages as immutable knowledge context
|
|
107
|
+
const { listPages, readPage } = await import("../wiki/fs.js");
|
|
108
|
+
const wikiPrefix = `squads/${squadSlug}`;
|
|
109
|
+
let wikiKnowledge = "";
|
|
110
|
+
try {
|
|
111
|
+
const pages = await listPages(wikiPrefix);
|
|
112
|
+
const pageContents = [];
|
|
113
|
+
for (const page of pages.slice(0, 20)) { // Cap at 20 pages to avoid token overload
|
|
114
|
+
try {
|
|
115
|
+
const content = await readPage(`${wikiPrefix}/${page}`);
|
|
116
|
+
pageContents.push(`### ${page}\n${content}`);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Skip unreadable pages
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (pageContents.length > 0) {
|
|
123
|
+
wikiKnowledge = `\n## Squad Knowledge Base\nThe following wiki pages contain important decisions, conventions, and context for this squad. Follow these as authoritative guidance:\n\n${pageContents.join("\n\n---\n\n")}\n`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Wiki not available — proceed without
|
|
128
|
+
}
|
|
105
129
|
const systemMessage = `# Squad Team Lead: ${lead.character_name}
|
|
106
130
|
|
|
107
131
|
You are ${lead.character_name}, the team lead for this squad. Your role is STRICTLY coordination — you do NOT write code, tests, or implementation of any kind.
|
|
@@ -129,7 +153,9 @@ ${agentRoster}
|
|
|
129
153
|
- Use \`--comment\` with "LGTM" for approvals (not \`--approve\`)
|
|
130
154
|
- Always use the gh CLI for GitHub interactions
|
|
131
155
|
- Merge criteria: all veto-capable members have posted approving comments + CI passes + no conflicts
|
|
132
|
-
|
|
156
|
+
- When work is complete, ALWAYS notify the user via feed_post with a summary of what was done
|
|
157
|
+
- Consult the squad wiki (wiki_read, wiki_search) for additional context beyond what's provided below
|
|
158
|
+
${wikiKnowledge}
|
|
133
159
|
${lead.persona ? `## Personality:\n${lead.persona}` : ""}
|
|
134
160
|
`;
|
|
135
161
|
let result;
|
|
@@ -169,6 +195,11 @@ ${lead.persona ? `## Personality:\n${lead.persona}` : ""}
|
|
|
169
195
|
agent: lead.character_name,
|
|
170
196
|
role: lead.role_title,
|
|
171
197
|
task,
|
|
198
|
+
attachments: attachments.map((attachment) => ({
|
|
199
|
+
name: attachment.name,
|
|
200
|
+
mimeType: attachment.mimeType,
|
|
201
|
+
size: attachment.size,
|
|
202
|
+
})),
|
|
172
203
|
});
|
|
173
204
|
// Capture streaming message deltas and broadcast via SSE
|
|
174
205
|
let accumulatedMessage = "";
|
|
@@ -186,7 +217,10 @@ ${lead.persona ? `## Personality:\n${lead.persona}` : ""}
|
|
|
186
217
|
}
|
|
187
218
|
});
|
|
188
219
|
try {
|
|
189
|
-
const response = await session.sendAndWait({
|
|
220
|
+
const response = await session.sendAndWait({
|
|
221
|
+
prompt: `Task delegated to you:\n\n${task}${buildAttachmentSummary(attachments)}`,
|
|
222
|
+
attachments: toCopilotBlobAttachments(attachments),
|
|
223
|
+
}, 600_000);
|
|
190
224
|
result = response?.data?.content ?? "Task completed (no response content).";
|
|
191
225
|
// Record the final message event if we have meaningful content
|
|
192
226
|
if (accumulatedMessage.trim()) {
|
|
@@ -4,6 +4,7 @@ import { getLeadForSquad, getAgentsForSquad, getSquad } from "../store/squads.js
|
|
|
4
4
|
import { selectModel } from "./model-router.js";
|
|
5
5
|
import { postFeedItem } from "../store/feed.js";
|
|
6
6
|
import { attachTokenTracker } from "./token-tracker.js";
|
|
7
|
+
import { buildAttachmentSummary, toCopilotBlobAttachments } from "../chat/attachments.js";
|
|
7
8
|
function buildFacilitatorPrompt(lead, agents, task) {
|
|
8
9
|
const roster = agents
|
|
9
10
|
.filter((a) => !a.is_lead)
|
|
@@ -76,7 +77,7 @@ Keep your response focused and concise — this is a planning meeting, not imple
|
|
|
76
77
|
${agent.persona ? `\n## Your Style:\n${agent.persona}` : ""}
|
|
77
78
|
`;
|
|
78
79
|
}
|
|
79
|
-
export async function planningMeeting(squadId, task) {
|
|
80
|
+
export async function planningMeeting(squadId, task, attachments = []) {
|
|
80
81
|
const lead = getLeadForSquad(squadId);
|
|
81
82
|
if (!lead) {
|
|
82
83
|
throw new Error("Squad has no team lead. Add a lead agent first.");
|
|
@@ -99,7 +100,10 @@ export async function planningMeeting(squadId, task) {
|
|
|
99
100
|
});
|
|
100
101
|
const flushTokens = attachTokenTracker(session, { squadId, agentId: agent.id });
|
|
101
102
|
try {
|
|
102
|
-
const response = await session.sendAndWait({
|
|
103
|
+
const response = await session.sendAndWait({
|
|
104
|
+
prompt: `Please provide your planning input for this task.${buildAttachmentSummary(attachments)}`,
|
|
105
|
+
attachments: toCopilotBlobAttachments(attachments),
|
|
106
|
+
}, 60_000);
|
|
103
107
|
return {
|
|
104
108
|
agent: agent.character_name,
|
|
105
109
|
role: agent.role_title,
|
|
@@ -134,7 +138,10 @@ export async function planningMeeting(squadId, task) {
|
|
|
134
138
|
let plan;
|
|
135
139
|
try {
|
|
136
140
|
const prompt = `Here is the input gathered from your team:\n\n${inputsSummary}\n\nNow synthesize this into a clear, structured action plan.`;
|
|
137
|
-
const response = await facilitatorSession.sendAndWait({
|
|
141
|
+
const response = await facilitatorSession.sendAndWait({
|
|
142
|
+
prompt,
|
|
143
|
+
attachments: toCopilotBlobAttachments(attachments),
|
|
144
|
+
}, 120_000);
|
|
138
145
|
plan = response?.data?.content ?? "Planning meeting completed but no plan was produced.";
|
|
139
146
|
}
|
|
140
147
|
finally {
|
|
@@ -146,8 +153,8 @@ export async function planningMeeting(squadId, task) {
|
|
|
146
153
|
participants: [lead.character_name, ...inputs.map((i) => i.agent)],
|
|
147
154
|
};
|
|
148
155
|
}
|
|
149
|
-
export async function squadMeeting(squadId, task, executeAfter) {
|
|
150
|
-
const result = await planningMeeting(squadId, task);
|
|
156
|
+
export async function squadMeeting(squadId, task, executeAfter, attachments = []) {
|
|
157
|
+
const result = await planningMeeting(squadId, task, attachments);
|
|
151
158
|
const summary = `## Planning Meeting Complete\n\n**Participants:** ${result.participants.join(", ")}\n\n${result.plan}`;
|
|
152
159
|
if (!executeAfter) {
|
|
153
160
|
// Post to feed and wait for user to trigger execution
|
|
@@ -159,7 +166,7 @@ export async function squadMeeting(squadId, task, executeAfter) {
|
|
|
159
166
|
// Execute: delegate with the plan as additional context
|
|
160
167
|
const { delegateTask } = await import("./agents.js");
|
|
161
168
|
const enrichedTask = `${task}\n\n---\n## Approved Plan (from team meeting)\n${result.plan}`;
|
|
162
|
-
const execResult = await delegateTask(squadId, enrichedTask);
|
|
169
|
+
const execResult = await delegateTask(squadId, enrichedTask, undefined, attachments);
|
|
163
170
|
return `Meeting held, then executed.\n\n${summary}\n\n---\n## Execution Result\n${execResult}`;
|
|
164
171
|
}
|
|
165
172
|
//# sourceMappingURL=ceremonies.js.map
|
|
@@ -7,9 +7,11 @@ import { loadSkillDirectories } from "./skills.js";
|
|
|
7
7
|
import { getMcpServersForSession } from "../mcp/registry.js";
|
|
8
8
|
import { resetClient } from "./client.js";
|
|
9
9
|
import { addAuditEntry } from "../store/audit-log.js";
|
|
10
|
+
import { buildAttachmentSummary, toCopilotBlobAttachments, } from "../chat/attachments.js";
|
|
10
11
|
let orchestratorSession;
|
|
11
12
|
let sessionCreatePromise;
|
|
12
13
|
let healthCheckInterval;
|
|
14
|
+
let activeMessageAttachments = [];
|
|
13
15
|
const messageQueue = [];
|
|
14
16
|
let processing = false;
|
|
15
17
|
export async function initOrchestrator(client, opts) {
|
|
@@ -82,9 +84,17 @@ function startHealthCheck(client, opts) {
|
|
|
82
84
|
}, 30_000);
|
|
83
85
|
healthCheckInterval.unref();
|
|
84
86
|
}
|
|
85
|
-
export async function sendToOrchestrator(prompt, source, callback) {
|
|
86
|
-
addAuditEntry("message_received", `Message from ${source}: ${prompt.slice(0, 200)}`, {
|
|
87
|
-
|
|
87
|
+
export async function sendToOrchestrator(prompt, source, callback, attachments = []) {
|
|
88
|
+
addAuditEntry("message_received", `Message from ${source}: ${prompt.slice(0, 200)}`, {
|
|
89
|
+
source,
|
|
90
|
+
prompt: prompt.slice(0, 1000),
|
|
91
|
+
attachments: attachments.map((attachment) => ({
|
|
92
|
+
name: attachment.name,
|
|
93
|
+
mimeType: attachment.mimeType,
|
|
94
|
+
size: attachment.size,
|
|
95
|
+
})),
|
|
96
|
+
});
|
|
97
|
+
messageQueue.push({ prompt, source, callback, attachments });
|
|
88
98
|
if (!processing)
|
|
89
99
|
processQueue();
|
|
90
100
|
}
|
|
@@ -109,7 +119,8 @@ async function executeOnSession(msg) {
|
|
|
109
119
|
msg.callback("Error: Orchestrator session not initialized.", true);
|
|
110
120
|
return;
|
|
111
121
|
}
|
|
112
|
-
|
|
122
|
+
activeMessageAttachments = msg.attachments;
|
|
123
|
+
const taggedPrompt = `[via ${msg.source}] ${msg.prompt}${buildAttachmentSummary(msg.attachments)}`;
|
|
113
124
|
let accumulated = "";
|
|
114
125
|
const unsubscribe = orchestratorSession.on("assistant.message_delta", (event) => {
|
|
115
126
|
const delta = event.data?.deltaContent ?? "";
|
|
@@ -117,11 +128,15 @@ async function executeOnSession(msg) {
|
|
|
117
128
|
msg.callback(accumulated, false);
|
|
118
129
|
});
|
|
119
130
|
try {
|
|
120
|
-
const response = await orchestratorSession.sendAndWait({
|
|
131
|
+
const response = await orchestratorSession.sendAndWait({
|
|
132
|
+
prompt: taggedPrompt,
|
|
133
|
+
attachments: toCopilotBlobAttachments(msg.attachments),
|
|
134
|
+
}, 600_000);
|
|
121
135
|
const finalContent = response?.data?.content ?? accumulated;
|
|
122
136
|
msg.callback(finalContent, true);
|
|
123
137
|
}
|
|
124
138
|
finally {
|
|
139
|
+
activeMessageAttachments = [];
|
|
125
140
|
unsubscribe();
|
|
126
141
|
}
|
|
127
142
|
}
|
|
@@ -132,4 +147,7 @@ export function feedAgentResult(taskId, agentName, result, callback) {
|
|
|
132
147
|
export function getOrchestratorSession() {
|
|
133
148
|
return orchestratorSession;
|
|
134
149
|
}
|
|
150
|
+
export function getActiveMessageAttachments() {
|
|
151
|
+
return activeMessageAttachments;
|
|
152
|
+
}
|
|
135
153
|
//# sourceMappingURL=orchestrator.js.map
|
package/dist/copilot/tools.js
CHANGED
|
@@ -159,8 +159,19 @@ export function createTools() {
|
|
|
159
159
|
}),
|
|
160
160
|
handler: async ({ squad_id, task, instance_id }) => {
|
|
161
161
|
const { delegateTask } = await import("./agents.js");
|
|
162
|
-
|
|
163
|
-
const
|
|
162
|
+
const { getActiveMessageAttachments } = await import("./orchestrator.js");
|
|
163
|
+
const attachments = getActiveMessageAttachments();
|
|
164
|
+
addAuditEntry("task_delegated", `Task delegated to squad ${squad_id}: ${task.slice(0, 200)}`, {
|
|
165
|
+
squad_id,
|
|
166
|
+
task: task.slice(0, 1000),
|
|
167
|
+
instance_id,
|
|
168
|
+
attachments: attachments.map((attachment) => ({
|
|
169
|
+
name: attachment.name,
|
|
170
|
+
mimeType: attachment.mimeType,
|
|
171
|
+
size: attachment.size,
|
|
172
|
+
})),
|
|
173
|
+
}, { squad_id });
|
|
174
|
+
const result = await delegateTask(squad_id, task, instance_id, attachments);
|
|
164
175
|
return result;
|
|
165
176
|
},
|
|
166
177
|
}),
|
|
@@ -175,8 +186,19 @@ export function createTools() {
|
|
|
175
186
|
}),
|
|
176
187
|
handler: async ({ squad_id, task, execute_after }) => {
|
|
177
188
|
const { squadMeeting } = await import("./ceremonies.js");
|
|
178
|
-
|
|
179
|
-
|
|
189
|
+
const { getActiveMessageAttachments } = await import("./orchestrator.js");
|
|
190
|
+
const attachments = getActiveMessageAttachments();
|
|
191
|
+
addAuditEntry("squad_meeting", `Planning meeting started for squad ${squad_id}: ${task.slice(0, 200)}`, {
|
|
192
|
+
squad_id,
|
|
193
|
+
task: task.slice(0, 1000),
|
|
194
|
+
execute_after,
|
|
195
|
+
attachments: attachments.map((attachment) => ({
|
|
196
|
+
name: attachment.name,
|
|
197
|
+
mimeType: attachment.mimeType,
|
|
198
|
+
size: attachment.size,
|
|
199
|
+
})),
|
|
200
|
+
}, { squad_id });
|
|
201
|
+
return await squadMeeting(squad_id, task, execute_after, attachments);
|
|
180
202
|
},
|
|
181
203
|
}),
|
|
182
204
|
defineTool("squad_task_status", {
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { getDb } from "./db.js";
|
|
3
|
+
function parseAttachments(value) {
|
|
4
|
+
if (!value)
|
|
5
|
+
return [];
|
|
6
|
+
try {
|
|
7
|
+
const parsed = JSON.parse(value);
|
|
8
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
3
14
|
function toMessage(row) {
|
|
4
15
|
return {
|
|
5
16
|
id: row.id,
|
|
@@ -7,6 +18,7 @@ function toMessage(row) {
|
|
|
7
18
|
role: row.role,
|
|
8
19
|
content: row.content,
|
|
9
20
|
source: row.source,
|
|
21
|
+
attachments: parseAttachments(row.attachments),
|
|
10
22
|
createdAt: row.created_at,
|
|
11
23
|
};
|
|
12
24
|
}
|
|
@@ -19,10 +31,10 @@ function toSummary(row) {
|
|
|
19
31
|
updatedAt: row.updated_at,
|
|
20
32
|
};
|
|
21
33
|
}
|
|
22
|
-
export function saveMessage(conversationId, role, content, source) {
|
|
34
|
+
export function saveMessage(conversationId, role, content, source, attachments = []) {
|
|
23
35
|
const db = getDb();
|
|
24
36
|
const id = randomUUID();
|
|
25
|
-
db.prepare("INSERT INTO conversation_messages (id, conversation_id, role, content, source) VALUES (?, ?, ?, ?, ?)").run(id, conversationId, role, content, source);
|
|
37
|
+
db.prepare("INSERT INTO conversation_messages (id, conversation_id, role, content, source, attachments) VALUES (?, ?, ?, ?, ?, ?)").run(id, conversationId, role, content, source, JSON.stringify(attachments));
|
|
26
38
|
const row = db
|
|
27
39
|
.prepare("SELECT * FROM conversation_messages WHERE id = ?")
|
|
28
40
|
.get(id);
|
package/dist/store/db.js
CHANGED
|
@@ -239,6 +239,13 @@ function runMigrations(db) {
|
|
|
239
239
|
`);
|
|
240
240
|
setSchemaVersion(db, 7);
|
|
241
241
|
}
|
|
242
|
+
if (version < 8) {
|
|
243
|
+
db.exec(`
|
|
244
|
+
ALTER TABLE conversation_messages
|
|
245
|
+
ADD COLUMN attachments TEXT NOT NULL DEFAULT '[]';
|
|
246
|
+
`);
|
|
247
|
+
setSchemaVersion(db, 8);
|
|
248
|
+
}
|
|
242
249
|
}
|
|
243
250
|
function getSchemaVersion(db) {
|
|
244
251
|
const row = db.prepare("SELECT value FROM meta WHERE key = 'schema_version'").get();
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{l as W,o as X,s as Z,u as a,k as d,h as o,n as P,D as A,a as V,m as S,z as l,T as f,N as T,F as h,w,O as F,j as b,v as n,g as _,q as ee}from"./index-
|
|
1
|
+
import{l as W,o as X,s as Z,u as a,k as d,h as o,n as P,D as A,a as V,m as S,z as l,T as f,N as T,F as h,w,O as F,j as b,v as n,g as _,q as ee}from"./index-DI8QgDXv.js";import{b as L}from"./api-2XP2yG1A.js";/**
|
|
2
2
|
* @license lucide-vue-next v0.474.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import{l as U,o as R,G as W,Q as B,s as Y,u as a,k as l,h as o,D as t,j as y,F as T,w as A,z as v,n as $,T as G,O as Q,i as h,S as J,d as Z,q as k,v as x,g as I,f as ee,p as E}from"./index-DI8QgDXv.js";import{F as z}from"./file-text-Dn2yDZCL.js";import{X as te}from"./x-Cw7ZjfeL.js";/**
|
|
2
|
+
* @license lucide-vue-next v0.474.0 - ISC
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the ISC license.
|
|
5
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/const F=U("ImageIcon",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",ry:"2",key:"1m3agn"}],["circle",{cx:"9",cy:"9",r:"2",key:"af1f0g"}],["path",{d:"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21",key:"1xmnt7"}]]);/**
|
|
7
|
+
* @license lucide-vue-next v0.474.0 - ISC
|
|
8
|
+
*
|
|
9
|
+
* This source code is licensed under the ISC license.
|
|
10
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
11
|
+
*/const se=U("PaperclipIcon",[["path",{d:"M13.234 20.252 21 12.3",key:"1cbrk9"}],["path",{d:"m16 6-8.414 8.586a2 2 0 0 0 0 2.828 2 2 0 0 0 2.828 0l8.414-8.586a4 4 0 0 0 0-5.656 4 4 0 0 0-5.656 0l-8.415 8.585a6 6 0 1 0 8.486 8.486",key:"1pkts6"}]]),L=10*1024*1024,P=25*1024*1024;function _(n){return n<1024?`${n} B`:n<1024*1024?`${(n/1024).toFixed(1)} KB`:`${(n/(1024*1024)).toFixed(1)} MB`}function S(n){return n.mimeType.startsWith("image/")}function ae(n){return`data:${n.mimeType};base64,${n.content}`}async function ne(n){const i=await new Promise((d,g)=>{const m=new FileReader;m.onload=()=>d(String(m.result??"")),m.onerror=()=>g(new Error("Unable to read file")),m.readAsDataURL(n)}),c=i.indexOf(",");if(c===-1)throw new Error(`Unable to parse file content for ${n.name}`);return{name:n.name,mimeType:n.type||"application/octet-stream",size:n.size,content:i.slice(c+1)}}function oe(n){for(const c of n)if(c.size>L)return{ok:!1,error:`"${c.name}" exceeds the 10MB per-file limit.`};return n.reduce((c,d)=>c+d.size,0)>P?{ok:!1,error:"Attachments exceed the 25MB per-message limit."}:{ok:!0}}const re={class:"flex flex-col h-full"},le={key:0,class:"flex items-center justify-center h-full"},ce={key:0,class:"mb-2 space-y-2"},ie=["src","alt"],ue={class:"flex items-center gap-2 text-xs"},de={class:"truncate"},me={class:"opacity-70"},pe={key:2,class:"text-muted-foreground"},fe={key:3,class:"inline-block w-2 h-4 bg-current animate-pulse ml-1"},ve={key:0,class:"mb-2 space-y-2"},he={class:"flex flex-wrap gap-2"},ge={class:"max-w-[170px] truncate"},ye={class:"opacity-70"},xe=["onClick"],_e={class:"text-xs text-muted-foreground"},be={key:1,class:"text-xs text-destructive mb-2"},ke={class:"flex gap-2 items-end"},we=["disabled"],Te=["disabled"],Ce=R({__name:"ChatView",setup(n){const i=W(),c=x(""),d=x(""),g=x(),m=x(),p=x([]),b=x(!1),j=I(()=>p.value.reduce((e,r)=>e+r.size,0)),D=I(()=>!i.isStreaming&&(c.value.trim().length>0||p.value.length>0));async function C(e){if(!e||e.length===0)return;d.value="";const r=[];try{for(const f of Array.from(e))r.push(await ne(f))}catch(f){d.value=(f==null?void 0:f.message)??"Unable to read one or more files.";return}const s=[...p.value,...r],u=oe(s);if(!u.ok){d.value=u.error;return}p.value=s,m.value&&(m.value.value="")}function N(e){p.value.splice(e,1),d.value=""}function O(){var e;(e=m.value)==null||e.click()}function V(e){const r=e.target;C((r==null?void 0:r.files)??null)}async function M(){if(!D.value)return;const e=c.value.trim(),r=[...p.value],s=e||"Please review the attached file(s).";c.value="",p.value=[],d.value="",await i.sendMessage(s,r)}function w(){g.value&&(g.value.scrollTop=g.value.scrollHeight)}function K(e){e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),M())}function q(e){e.preventDefault(),b.value=!0}function H(e){e.preventDefault(),b.value=!1}async function X(e){var r;e.preventDefault(),b.value=!1,await C(((r=e.dataTransfer)==null?void 0:r.files)??null)}return B(()=>i.messages.map(e=>e.content),async()=>{await E(),w()},{deep:!0}),B(()=>i.messages.length,async()=>{await E(),w()}),Y(()=>w()),(e,r)=>(a(),l("div",re,[o("div",{ref_key:"messagesContainer",ref:g,class:"flex-1 overflow-y-auto p-4 space-y-4"},[t(i).messages.length===0?(a(),l("div",le,[...r[1]||(r[1]=[o("div",{class:"text-center text-muted-foreground"},[o("div",{class:"text-4xl mb-3"},"🤖"),o("p",{class:"text-lg font-medium"},"Welcome to IO"),o("p",{class:"text-sm mt-1"},"Send a message to get started.")],-1)])])):y("",!0),(a(!0),l(T,null,A(t(i).messages,s=>(a(),l("div",{key:s.id,class:k(["flex",s.role==="user"?"justify-end":"justify-start"])},[o("div",{class:k(["max-w-[75%] rounded-lg px-4 py-2 text-sm",s.role==="user"?"bg-primary text-primary-foreground":"bg-muted text-foreground"])},[s.attachments.length>0?(a(),l("div",ce,[(a(!0),l(T,null,A(s.attachments,(u,f)=>(a(),l("div",{key:`${s.id}-${f}`,class:"rounded border border-border/50 p-2 bg-background/70 text-foreground"},[t(S)(u)?(a(),l("img",{key:0,src:t(ae)(u),alt:u.name,class:"max-h-44 rounded mb-1 object-contain"},null,8,ie)):y("",!0),o("div",ue,[t(S)(u)?(a(),h(t(F),{key:0,class:"w-3.5 h-3.5"})):(a(),h(t(z),{key:1,class:"w-3.5 h-3.5"})),o("span",de,v(u.name),1),o("span",me,v(t(_)(u.size)),1)])]))),128))])):y("",!0),s.content?(a(),h(ee,{key:1,content:s.content,class:k(s.role==="user"?"prose-invert":"")},null,8,["content","class"])):(a(),l("span",pe,"...")),s.streaming?(a(),l("div",fe)):y("",!0)],2)],2))),128))],512),o("div",{class:k(["border-t border-border p-4",b.value?"bg-accent/40":""]),onDragover:q,onDragleave:H,onDrop:X},[o("input",{ref_key:"fileInput",ref:m,type:"file",multiple:"",class:"hidden",onChange:V},null,544),p.value.length>0?(a(),l("div",ve,[o("div",he,[(a(!0),l(T,null,A(p.value,(s,u)=>(a(),l("div",{key:`${s.name}-${u}`,class:"flex items-center gap-2 rounded border border-border px-2 py-1 text-xs bg-muted"},[t(S)(s)?(a(),h(t(F),{key:0,class:"w-3.5 h-3.5"})):(a(),h(t(z),{key:1,class:"w-3.5 h-3.5"})),o("span",ge,v(s.name),1),o("span",ye,v(t(_)(s.size)),1),o("button",{class:"hover:text-destructive",onClick:f=>N(u)},[$(t(te),{class:"w-3.5 h-3.5"})],8,xe)]))),128))]),o("p",_e,v(t(_)(j.value))+" attached · Max per file "+v(t(_)(t(L)))+" · Max total "+v(t(_)(t(P))),1)])):y("",!0),d.value?(a(),l("p",be,v(d.value),1)):y("",!0),o("div",ke,[o("button",{class:"rounded-md border border-input p-2 hover:bg-accent disabled:opacity-50",disabled:t(i).isStreaming,onClick:O,title:"Attach files"},[$(t(se),{class:"w-4 h-4"})],8,we),G(o("textarea",{"onUpdate:modelValue":r[0]||(r[0]=s=>c.value=s),onKeydown:K,placeholder:"Send a message...",rows:"1",class:"flex-1 resize-none rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring min-h-[40px] max-h-[120px]"},null,544),[[Q,c.value]]),o("button",{onClick:M,disabled:!D.value,class:"rounded-md bg-primary text-primary-foreground p-2 hover:bg-primary/90 disabled:opacity-50 transition-colors"},[t(i).isStreaming?(a(),h(t(Z),{key:1,class:"w-4 h-4"})):(a(),h(t(J),{key:0,class:"w-4 h-4"}))],8,Te)])],34)]))}});export{Ce as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{l as M,o as D,s as N,u as o,k as n,h as a,F as y,w as b,n as c,D as i,I as V,v as d,g as z,q as f,z as v,r as B,W as k,j as q,f as j}from"./index-
|
|
1
|
+
import{l as M,o as D,s as N,u as o,k as n,h as a,F as y,w as b,n as c,D as i,I as V,v as d,g as z,q as f,z as v,r as B,W as k,j as q,f as j}from"./index-DI8QgDXv.js";import{b as C,c as P,a as T}from"./api-2XP2yG1A.js";import{g as W}from"./squad-colors-B8B_Y-lz.js";import{T as A}from"./trash-2-C86P23n5.js";/**
|
|
2
2
|
* @license lucide-vue-next v0.474.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as U,Q as q,s as z,u as r,k as n,h as t,n as d,D as c,H as V,T,O as D,M as P,j as x,F as $,w as j,q as p,z as g,v as l,g as Q,i as E,f as G}from"./index-
|
|
1
|
+
import{o as U,Q as q,s as z,u as r,k as n,h as t,n as d,D as c,H as V,T,O as D,M as P,j as x,F as $,w as j,q as p,z as g,v as l,g as Q,i as E,f as G}from"./index-DI8QgDXv.js";import{b as A,a as O}from"./api-2XP2yG1A.js";import{S as R}from"./search-BnsxENnH.js";import{A as I}from"./arrow-left-BsbWSKAE.js";import{T as J}from"./trash-2-C86P23n5.js";const K={class:"flex h-full"},W={class:"p-3 border-b border-border space-y-2"},X={class:"flex items-center gap-2"},Y={class:"relative"},Z={class:"flex gap-2"},ee={class:"flex-1"},te={class:"flex-1"},se={class:"flex-1 overflow-y-auto"},oe={key:0,class:"p-4 text-xs text-muted-foreground"},re={key:1,class:"flex flex-col items-center justify-center h-full p-6 text-center text-muted-foreground"},ne=["onClick"],ae={class:"flex-1 min-w-0"},le={class:"text-xs text-foreground line-clamp-2"},ie={class:"flex items-center gap-2 mt-1"},ue={class:"text-xs text-muted-foreground"},de={class:"text-xs text-muted-foreground"},ce=["onClick"],fe={key:2,class:"p-3 text-center"},ve={key:0,class:"flex-1 flex flex-col"},me={class:"flex items-center gap-2 px-4 py-2 border-b border-border"},xe={class:"text-sm font-medium text-muted-foreground"},pe={class:"flex-1 overflow-y-auto p-4 space-y-4"},ge={key:0,class:"text-center text-xs text-muted-foreground py-8"},he={class:"text-xs mt-1 opacity-60"},ye={key:1,class:"hidden md:flex flex-1 items-center justify-center text-muted-foreground"},be={class:"text-center"},_e=50,Le=U({__name:"HistoryView",setup(we){const a=l([]),h=l(0),y=l(!0),f=l(""),v=l(""),m=l(""),u=l(null),b=l([]),_=l(!1),w=l(0);async function k(o=!0){y.value=!0;try{o&&(w.value=0,a.value=[]);const e=new URLSearchParams;f.value&&e.set("q",f.value),v.value&&e.set("from",v.value),m.value&&e.set("to",m.value+"T23:59:59"),e.set("limit",String(_e)),e.set("offset",String(w.value));const i=await A(`/history?${e.toString()}`);a.value=o?i.items:[...a.value,...i.items],h.value=i.total,w.value+=i.items.length}finally{y.value=!1}}async function B(o){u.value=o,_.value=!0;try{b.value=await A(`/history/${o}`)}finally{_.value=!1}}function L(){u.value=null,b.value=[]}async function F(o,e){e.stopPropagation(),confirm("Delete this conversation?")&&(await O(`/history/${o}`),a.value=a.value.filter(i=>i.id!==o),h.value=Math.max(0,h.value-1),u.value===o&&L())}function C(o){return new Date(o).toLocaleString(void 0,{year:"numeric",month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}function H(o,e=100){return o.length>e?o.slice(0,e)+"…":o}const N=Q(()=>a.value.length<h.value);let S=null;return q([f,v,m],()=>{S&&clearTimeout(S),S=setTimeout(()=>k(!0),300)}),z(()=>k(!0)),(o,e)=>{var i;return r(),n("div",K,[t("div",{class:p(["flex flex-col border-r border-border",u.value?"hidden md:flex w-80 shrink-0":"flex-1"])},[t("div",W,[t("div",X,[d(c(V),{class:"w-4 h-4 text-muted-foreground"}),e[4]||(e[4]=t("span",{class:"text-sm font-medium"},"Conversation History",-1))]),t("div",Y,[d(c(R),{class:"absolute left-2.5 top-2.5 w-3.5 h-3.5 text-muted-foreground"}),T(t("input",{"onUpdate:modelValue":e[0]||(e[0]=s=>f.value=s),placeholder:"Search conversations...",class:"w-full rounded-md border border-input bg-background pl-8 pr-3 py-2 text-xs focus:outline-none focus:ring-1 focus:ring-ring"},null,512),[[D,f.value]])]),t("div",Z,[t("div",ee,[e[5]||(e[5]=t("label",{class:"text-xs text-muted-foreground block mb-1"},"From",-1)),T(t("input",{"onUpdate:modelValue":e[1]||(e[1]=s=>v.value=s),type:"date",class:"w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs focus:outline-none focus:ring-1 focus:ring-ring"},null,512),[[D,v.value]])]),t("div",te,[e[6]||(e[6]=t("label",{class:"text-xs text-muted-foreground block mb-1"},"To",-1)),T(t("input",{"onUpdate:modelValue":e[2]||(e[2]=s=>m.value=s),type:"date",class:"w-full rounded-md border border-input bg-background px-2 py-1.5 text-xs focus:outline-none focus:ring-1 focus:ring-ring"},null,512),[[D,m.value]])])])]),t("div",se,[y.value&&a.value.length===0?(r(),n("div",oe," Loading... ")):a.value.length===0?(r(),n("div",re,[d(c(P),{class:"w-10 h-10 mb-3 opacity-40"}),e[7]||(e[7]=t("p",{class:"text-sm"},"No conversations found.",-1)),e[8]||(e[8]=t("p",{class:"text-xs mt-1"},"Start chatting to build up your history.",-1))])):x("",!0),(r(!0),n($,null,j(a.value,s=>(r(),n("div",{key:s.id,class:p(["group flex items-start gap-2 px-3 py-3 border-b border-border cursor-pointer hover:bg-accent/50 transition-colors",u.value===s.id?"bg-accent":""]),onClick:M=>B(s.id)},[t("div",ae,[t("p",le,g(H(s.preview)),1),t("div",ie,[t("span",ue,g(C(s.updatedAt)),1),t("span",de,"· "+g(s.messageCount)+" msgs",1)])]),t("button",{class:"opacity-0 group-hover:opacity-100 p-1 rounded hover:bg-destructive/10 text-muted-foreground hover:text-destructive transition-all",title:"Delete",onClick:M=>F(s.id,M)},[d(c(J),{class:"w-3.5 h-3.5"})],8,ce)],10,ne))),128)),N.value?(r(),n("div",fe,[t("button",{class:"text-xs text-muted-foreground hover:text-foreground underline",onClick:e[3]||(e[3]=s=>k(!1))}," Load more ")])):x("",!0)])],2),u.value?(r(),n("div",ve,[t("div",me,[t("button",{class:"p-1.5 rounded hover:bg-accent text-muted-foreground",title:"Back",onClick:L},[d(c(I),{class:"w-4 h-4"})]),t("span",xe,g(C(((i=a.value.find(s=>s.id===u.value))==null?void 0:i.startedAt)??"")),1)]),t("div",pe,[_.value?(r(),n("div",ge," Loading... ")):x("",!0),(r(!0),n($,null,j(b.value,s=>(r(),n("div",{key:s.id,class:p(["flex",s.role==="user"?"justify-end":"justify-start"])},[t("div",{class:p(["max-w-[75%] rounded-lg px-4 py-2 text-sm",s.role==="user"?"bg-blue-600 text-white":"bg-muted text-foreground"])},[s.content?(r(),E(G,{key:0,content:s.content,class:p(s.role==="user"?"prose-invert":"")},null,8,["content","class"])):x("",!0),t("p",he,g(C(s.createdAt)),1)],2)],2))),128))])])):u.value?x("",!0):(r(),n("div",ye,[t("div",be,[d(c(V),{class:"w-12 h-12 mx-auto mb-3 opacity-50"}),e[9]||(e[9]=t("p",null,"Select a conversation to view",-1))])]))])}}});export{Le as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as b,E as v,u,k as i,h as t,n as y,W as w,T as m,O as p,z as f,j as h,D as g,v as d,_,K as S}from"./index-
|
|
1
|
+
import{o as b,E as v,u,k as i,h as t,n as y,W as w,T as m,O as p,z as f,j as h,D as g,v as d,_,K as S}from"./index-DI8QgDXv.js";const V={class:"min-h-screen flex items-center justify-center bg-background p-4"},k={class:"w-full max-w-sm space-y-8"},D={class:"text-center"},A={key:0,class:"text-sm text-destructive"},B=["disabled"],L=b({__name:"LoginView",setup(E){const o=v(),c=S(),r=d(""),n=d(""),s=d("");async function x(){s.value="";try{await o.login(r.value,n.value),c.push("/")}catch(l){s.value=l.message??"Login failed"}}return(l,e)=>(u(),i("div",V,[t("div",k,[t("div",D,[y(_,{size:56,class:"mx-auto mb-4"}),e[2]||(e[2]=t("h1",{class:"text-2xl font-bold bg-gradient-brand bg-clip-text text-transparent"}," IO ",-1)),e[3]||(e[3]=t("p",{class:"text-sm text-muted-foreground mt-1"},"Sign in to your dashboard",-1))]),t("form",{onSubmit:w(x,["prevent"]),class:"space-y-4 bg-card border border-border rounded-lg p-6"},[t("div",null,[e[4]||(e[4]=t("label",{class:"text-sm font-medium text-muted-foreground",for:"email"},"Email",-1)),m(t("input",{id:"email","onUpdate:modelValue":e[0]||(e[0]=a=>r.value=a),type:"email",required:"",class:"mt-1 w-full rounded-md border border-border bg-input px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring",placeholder:"you@example.com"},null,512),[[p,r.value]])]),t("div",null,[e[5]||(e[5]=t("label",{class:"text-sm font-medium text-muted-foreground",for:"password"},"Password",-1)),m(t("input",{id:"password","onUpdate:modelValue":e[1]||(e[1]=a=>n.value=a),type:"password",required:"",class:"mt-1 w-full rounded-md border border-border bg-input px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring",placeholder:"••••••••"},null,512),[[p,n.value]])]),s.value?(u(),i("div",A,f(s.value),1)):h("",!0),t("button",{type:"submit",disabled:g(o).loading,class:"btn-gradient w-full py-2.5"},f(g(o).loading?"Signing in...":"Sign In"),9,B)],32),e[6]||(e[6]=t("p",{class:"text-center text-xs text-muted-foreground"}," Personal AI Assistant Daemon ",-1))])]))}});export{L as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as k,s as C,u as a,k as n,h as t,n as m,D as p,m as h,T as u,O as v,N as S,j as _,c as V,F as N,w as M,v as i,z as b,q as y}from"./index-
|
|
1
|
+
import{o as k,s as C,u as a,k as n,h as t,n as m,D as p,m as h,T as u,O as v,N as S,j as _,c as V,F as N,w as M,v as i,z as b,q as y}from"./index-DI8QgDXv.js";import{b as P,c as T,d as U,a as $}from"./api-2XP2yG1A.js";import{P as D}from"./plus-BOXwfDpA.js";import{T as j}from"./trash-2-C86P23n5.js";const B={class:"p-6"},L={class:"flex items-center justify-between mb-6"},z={key:0,class:"border border-border rounded-lg p-4 mb-6 space-y-3"},A={class:"grid grid-cols-2 gap-3"},F={key:0},q={key:1},E={key:1,class:"text-muted-foreground"},G={key:2,class:"text-center py-12 text-muted-foreground"},O={key:3,class:"space-y-2"},R={class:"font-medium text-sm"},H={class:"ml-2 text-xs text-muted-foreground bg-secondary px-1.5 py-0.5 rounded"},I={class:"flex items-center gap-3"},J=["onClick"],K=["onClick"],ee=k({__name:"McpView",setup(Q){const d=i([]),c=i(!0),r=i(!1),o=i({name:"",type:"stdio",command:"",url:""});C(async()=>{try{d.value=await P("/mcp")}finally{c.value=!1}});async function f(l){await U(`/mcp/${l.id}`,{enabled:!l.enabled}),l.enabled=!l.enabled}async function x(l){await $(`/mcp/${l}`),d.value=d.value.filter(e=>e.id!==l)}async function g(){const l={name:o.value.name,type:o.value.type};o.value.type==="stdio"?l.command=o.value.command:l.url=o.value.url;const e=await T("/mcp",l);d.value.push(e),r.value=!1,o.value={name:"",type:"stdio",command:"",url:""}}return(l,e)=>(a(),n("div",B,[t("div",L,[e[6]||(e[6]=t("h1",{class:"text-2xl font-bold"},"MCP Servers",-1)),t("button",{onClick:e[0]||(e[0]=s=>r.value=!r.value),class:"inline-flex items-center gap-1 px-3 py-1.5 text-sm rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors"},[m(p(D),{class:"w-4 h-4"}),e[5]||(e[5]=h(" Add Server ",-1))])]),r.value?(a(),n("div",z,[t("div",A,[t("div",null,[e[7]||(e[7]=t("label",{class:"text-sm font-medium"},"Name",-1)),u(t("input",{"onUpdate:modelValue":e[1]||(e[1]=s=>o.value.name=s),class:"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"},null,512),[[v,o.value.name]])]),t("div",null,[e[9]||(e[9]=t("label",{class:"text-sm font-medium"},"Type",-1)),u(t("select",{"onUpdate:modelValue":e[2]||(e[2]=s=>o.value.type=s),class:"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"},[...e[8]||(e[8]=[t("option",{value:"stdio"},"stdio",-1),t("option",{value:"http"},"http",-1)])],512),[[S,o.value.type]])])]),o.value.type==="stdio"?(a(),n("div",F,[e[10]||(e[10]=t("label",{class:"text-sm font-medium"},"Command",-1)),u(t("input",{"onUpdate:modelValue":e[3]||(e[3]=s=>o.value.command=s),placeholder:"npx @my/mcp-server",class:"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"},null,512),[[v,o.value.command]])])):(a(),n("div",q,[e[11]||(e[11]=t("label",{class:"text-sm font-medium"},"URL",-1)),u(t("input",{"onUpdate:modelValue":e[4]||(e[4]=s=>o.value.url=s),placeholder:"https://...",class:"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"},null,512),[[v,o.value.url]])])),t("button",{onClick:g,class:"px-4 py-2 text-sm rounded-md bg-primary text-primary-foreground hover:bg-primary/90"},"Save")])):_("",!0),c.value?(a(),n("div",E,"Loading...")):d.value.length===0?(a(),n("div",G,[m(p(V),{class:"w-12 h-12 mx-auto mb-3 opacity-50"}),e[12]||(e[12]=t("p",null,"No MCP servers configured.",-1))])):(a(),n("div",O,[(a(!0),n(N,null,M(d.value,s=>(a(),n("div",{key:s.id,class:"flex items-center justify-between border border-border rounded-lg px-4 py-3"},[t("div",null,[t("span",R,b(s.name),1),t("span",H,b(s.type),1)]),t("div",I,[t("button",{onClick:w=>f(s),class:y(["relative w-10 h-5 rounded-full transition-colors",s.enabled?"bg-primary":"bg-muted"])},[t("span",{class:y(["absolute top-0.5 w-4 h-4 rounded-full bg-white transition-transform",s.enabled?"translate-x-5":"translate-x-0.5"])},null,2)],10,J),t("button",{onClick:w=>x(s.id),class:"p-1.5 rounded hover:bg-destructive/10 text-muted-foreground hover:text-destructive"},[m(p(j),{class:"w-4 h-4"})],8,K)])]))),128))]))]))}});export{ee as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{l as B,o as L,s as O,u as r,k as d,h as o,n as v,D as m,m as j,F as x,w as y,T as b,N as S,O as h,j as _,b as z,z as l,v as u,g as M,q as w,r as A}from"./index-
|
|
1
|
+
import{l as B,o as L,s as O,u as r,k as d,h as o,n as v,D as m,m as j,F as x,w as y,T as b,N as S,O as h,j as _,b as z,z as l,v as u,g as M,q as w,r as A}from"./index-DI8QgDXv.js";import{b as C,c as T,d as E,a as F}from"./api-2XP2yG1A.js";import{g as G}from"./squad-colors-B8B_Y-lz.js";import{P as R}from"./plus-BOXwfDpA.js";import{T as H}from"./trash-2-C86P23n5.js";/**
|
|
2
2
|
* @license lucide-vue-next v0.474.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as w,s as E,u as d,k as s,h as t,z as g,F as x,w as U,T as a,O as n,N as T,L as p,j as b,v as r,q as M}from"./index-
|
|
1
|
+
import{o as w,s as E,u as d,k as s,h as t,z as g,F as x,w as U,T as a,O as n,N as T,L as p,j as b,v as r,q as M}from"./index-DI8QgDXv.js";import{b as V,d as S}from"./api-2XP2yG1A.js";const z={class:"p-6"},A={class:"flex items-center justify-between mb-6"},N=["disabled"],B={key:0,class:"text-muted-foreground"},C={class:"flex gap-1 border-b border-border mb-6"},I=["onClick"],D={key:0,class:"space-y-4 max-w-lg"},K={class:"flex items-center gap-3"},L={key:1,class:"space-y-4 max-w-lg"},j={class:"flex items-center gap-3"},O={key:2,class:"space-y-4 max-w-lg"},q={key:3,class:"space-y-4 max-w-lg"},F={class:"flex items-center gap-3"},G={class:"flex items-center gap-3"},H=w({__name:"SettingsView",setup(P){const f=r(!0),i=r(!1),m=r(!1),u=r("general"),y=[{id:"general",label:"General"},{id:"telegram",label:"Telegram"},{id:"auth",label:"Auth"},{id:"advanced",label:"Advanced"}],o=r({defaultModel:"",port:3170,telegramEnabled:!1,telegramBotToken:"",authorizedUserId:null,supabaseUrl:"",supabaseAnonKey:"",authorizedEmail:"",backgroundNotifyMode:"meaningful",backgroundNotifyTelegram:!0,selfEditEnabled:!1,watchdogEnabled:!0});async function k(){f.value=!0;try{const v=await V("/settings");o.value=v}finally{f.value=!1}}async function c(){i.value=!0,m.value=!1;try{await S("/settings",o.value),m.value=!0,setTimeout(()=>m.value=!1,2e3)}finally{i.value=!1}}return E(k),(v,e)=>(d(),s("div",z,[t("div",A,[e[12]||(e[12]=t("h1",{class:"text-2xl font-bold"},"Settings",-1)),t("button",{onClick:c,disabled:i.value,class:"px-4 py-2 rounded-md bg-primary text-primary-foreground text-sm hover:bg-primary/90 disabled:opacity-50"},g(i.value?"Saving...":m.value?"Saved ✓":"Save"),9,N)]),f.value?(d(),s("div",B,"Loading...")):(d(),s(x,{key:1},[t("div",C,[(d(),s(x,null,U(y,l=>t("button",{key:l.id,onClick:R=>u.value=l.id,class:M(["px-4 py-2 text-sm font-medium border-b-2 transition-colors",u.value===l.id?"border-primary text-foreground":"border-transparent text-muted-foreground hover:text-foreground"])},g(l.label),11,I)),64))]),u.value==="general"?(d(),s("div",D,[t("div",null,[e[13]||(e[13]=t("label",{class:"text-sm font-medium"},"Default Model",-1)),a(t("input",{"onUpdate:modelValue":e[0]||(e[0]=l=>o.value.defaultModel=l),class:"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"},null,512),[[n,o.value.defaultModel]])]),t("div",null,[e[14]||(e[14]=t("label",{class:"text-sm font-medium"},"Port",-1)),a(t("input",{"onUpdate:modelValue":e[1]||(e[1]=l=>o.value.port=l),type:"number",class:"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"},null,512),[[n,o.value.port,void 0,{number:!0}]]),e[15]||(e[15]=t("p",{class:"text-xs text-muted-foreground mt-1"},"Requires restart to take effect",-1))]),t("div",null,[e[17]||(e[17]=t("label",{class:"text-sm font-medium"},"Background Notify Mode",-1)),a(t("select",{"onUpdate:modelValue":e[2]||(e[2]=l=>o.value.backgroundNotifyMode=l),class:"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"},[...e[16]||(e[16]=[t("option",{value:"all"},"All",-1),t("option",{value:"meaningful"},"Meaningful",-1),t("option",{value:"off"},"Off",-1)])],512),[[T,o.value.backgroundNotifyMode]])]),t("div",K,[a(t("input",{"onUpdate:modelValue":e[3]||(e[3]=l=>o.value.backgroundNotifyTelegram=l),type:"checkbox",id:"notifyTelegram",class:"rounded"},null,512),[[p,o.value.backgroundNotifyTelegram]]),e[18]||(e[18]=t("label",{for:"notifyTelegram",class:"text-sm font-medium"},"Send notifications via Telegram",-1))])])):b("",!0),u.value==="telegram"?(d(),s("div",L,[t("div",null,[e[19]||(e[19]=t("label",{class:"text-sm font-medium"},"Bot Token",-1)),a(t("input",{"onUpdate:modelValue":e[4]||(e[4]=l=>o.value.telegramBotToken=l),type:"password",placeholder:"Enter new token to update",class:"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"},null,512),[[n,o.value.telegramBotToken]])]),t("div",null,[e[20]||(e[20]=t("label",{class:"text-sm font-medium"},"Authorized User ID",-1)),a(t("input",{"onUpdate:modelValue":e[5]||(e[5]=l=>o.value.authorizedUserId=l),type:"number",placeholder:"123456789",class:"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"},null,512),[[n,o.value.authorizedUserId,void 0,{number:!0}]])]),t("div",j,[a(t("input",{"onUpdate:modelValue":e[6]||(e[6]=l=>o.value.telegramEnabled=l),type:"checkbox",id:"telegramEnabled",class:"rounded"},null,512),[[p,o.value.telegramEnabled]]),e[21]||(e[21]=t("label",{for:"telegramEnabled",class:"text-sm font-medium"},"Enable Telegram Bot",-1))])])):b("",!0),u.value==="auth"?(d(),s("div",O,[t("div",null,[e[22]||(e[22]=t("label",{class:"text-sm font-medium"},"Supabase URL",-1)),a(t("input",{"onUpdate:modelValue":e[7]||(e[7]=l=>o.value.supabaseUrl=l),placeholder:"https://your-project.supabase.co",class:"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"},null,512),[[n,o.value.supabaseUrl]])]),t("div",null,[e[23]||(e[23]=t("label",{class:"text-sm font-medium"},"Supabase Anon Key",-1)),a(t("input",{"onUpdate:modelValue":e[8]||(e[8]=l=>o.value.supabaseAnonKey=l),type:"password",placeholder:"Enter new key to update",class:"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"},null,512),[[n,o.value.supabaseAnonKey]])]),t("div",null,[e[24]||(e[24]=t("label",{class:"text-sm font-medium"},"Authorized Email",-1)),a(t("input",{"onUpdate:modelValue":e[9]||(e[9]=l=>o.value.authorizedEmail=l),type:"email",placeholder:"you@example.com",class:"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"},null,512),[[n,o.value.authorizedEmail]])])])):b("",!0),u.value==="advanced"?(d(),s("div",q,[t("div",F,[a(t("input",{"onUpdate:modelValue":e[10]||(e[10]=l=>o.value.selfEditEnabled=l),type:"checkbox",id:"selfEdit",class:"rounded"},null,512),[[p,o.value.selfEditEnabled]]),e[25]||(e[25]=t("div",null,[t("label",{for:"selfEdit",class:"text-sm font-medium"},"Self-Edit Mode"),t("p",{class:"text-xs text-muted-foreground"},"Allow IO to modify its own source code")],-1))]),t("div",G,[a(t("input",{"onUpdate:modelValue":e[11]||(e[11]=l=>o.value.watchdogEnabled=l),type:"checkbox",id:"watchdog",class:"rounded"},null,512),[[p,o.value.watchdogEnabled]]),e[26]||(e[26]=t("div",null,[t("label",{for:"watchdog",class:"text-sm font-medium"},"Watchdog"),t("p",{class:"text-xs text-muted-foreground"},"Monitor event loop and zombie instances")],-1))])])):b("",!0)],64))]))}});export{H as default};
|