@saadnvd1/agent-os 0.3.821 → 0.3.824
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/content/whats-new.json
CHANGED
|
@@ -1921,6 +1921,22 @@
|
|
|
1921
1921
|
"area": "AgentOS",
|
|
1922
1922
|
"text": "You can set your name in Settings, and AgentOS uses it in chats and notes instead of a fixed name.",
|
|
1923
1923
|
"url": "https://github.com/saadnvd1/agent-os/pull/550"
|
|
1924
|
+
},
|
|
1925
|
+
{
|
|
1926
|
+
"id": 551,
|
|
1927
|
+
"date": "2026-11-01",
|
|
1928
|
+
"kind": "fixed",
|
|
1929
|
+
"area": "Chat",
|
|
1930
|
+
"text": "Questions from GitHub Copilot chats now always show up at the latest message after the chat restarts, instead of going missing.",
|
|
1931
|
+
"url": "https://github.com/saadnvd1/agent-os/pull/551"
|
|
1932
|
+
},
|
|
1933
|
+
{
|
|
1934
|
+
"id": 552,
|
|
1935
|
+
"date": "2026-11-01",
|
|
1936
|
+
"kind": "fixed",
|
|
1937
|
+
"area": "Chat",
|
|
1938
|
+
"text": "Cancelled questions from GitHub Copilot chats now clear right away instead of staying on screen.",
|
|
1939
|
+
"url": "https://github.com/saadnvd1/agent-os/pull/552"
|
|
1924
1940
|
}
|
|
1925
1941
|
]
|
|
1926
1942
|
}
|
|
@@ -66,6 +66,8 @@ export function askUserQuestion(a: AskArgs): ChatQuestion {
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
const requestKey = (id: string | number) => `${typeof id}:${id}`;
|
|
70
|
+
|
|
69
71
|
const text = (t: string, isError = false): CallToolResult => ({
|
|
70
72
|
content: [{ type: "text", text: t }],
|
|
71
73
|
...(isError ? { isError } : {}),
|
|
@@ -105,8 +107,10 @@ export function isAskUserCall(call: P): boolean {
|
|
|
105
107
|
export class CopilotAskServer {
|
|
106
108
|
private http?: http.Server;
|
|
107
109
|
private secret = crypto.randomBytes(32).toString("hex");
|
|
108
|
-
private next = 0;
|
|
109
110
|
private live = new Set<{ close(): Promise<void> }>();
|
|
111
|
+
// Waiting ask_user calls by JSON-RPC request id. A cancel arrives as its
|
|
112
|
+
// own POST, on a fresh MCP server, so only this map can match it.
|
|
113
|
+
private waiting = new Map<string, string>();
|
|
110
114
|
|
|
111
115
|
constructor(private pending: PendingApprovals) {}
|
|
112
116
|
|
|
@@ -119,22 +123,49 @@ export class CopilotAskServer {
|
|
|
119
123
|
"Ask the user a question and wait for the answer. Use it when you need a decision or missing information instead of asking in plain text. Offer options for a choice (multiSelect to allow several); with no options the user types an answer. Blocks until they answer.",
|
|
120
124
|
inputSchema: askInput,
|
|
121
125
|
},
|
|
122
|
-
async (args) => {
|
|
126
|
+
async (args, extra) => {
|
|
123
127
|
const q = askUserQuestion(args);
|
|
124
|
-
|
|
125
|
-
|
|
128
|
+
// Unique across worker restarts: a card's id is its chat row, so a
|
|
129
|
+
// reused id would rewrite an old card up the history instead.
|
|
130
|
+
const id = `approval-ask-${crypto.randomUUID()}`;
|
|
131
|
+
const asked = this.pending.ask({
|
|
132
|
+
id,
|
|
126
133
|
toolName: "AskUserQuestion",
|
|
127
134
|
title: "Questions",
|
|
128
135
|
input: { questions: [q] },
|
|
129
136
|
questions: [q],
|
|
130
137
|
canAlways: false,
|
|
131
138
|
});
|
|
132
|
-
|
|
139
|
+
// Copilot gave up on the call (its timeout, or the connection went):
|
|
140
|
+
// nobody is waiting for this answer any more.
|
|
141
|
+
const expire = () => this.pending.respond(id, { decision: "expired" });
|
|
142
|
+
if (extra.signal.aborted) expire();
|
|
143
|
+
else extra.signal.addEventListener("abort", expire, { once: true });
|
|
144
|
+
const key = requestKey(extra.requestId);
|
|
145
|
+
this.waiting.set(key, id);
|
|
146
|
+
try {
|
|
147
|
+
return askUserResult(q, await asked);
|
|
148
|
+
} finally {
|
|
149
|
+
if (this.waiting.get(key) === id) this.waiting.delete(key);
|
|
150
|
+
}
|
|
133
151
|
}
|
|
134
152
|
);
|
|
135
153
|
return server;
|
|
136
154
|
}
|
|
137
155
|
|
|
156
|
+
// notifications/cancelled names the request to give up on. An id with no
|
|
157
|
+
// waiting call (answered already, or never ours) is ignored.
|
|
158
|
+
private cancelWaiting(body: unknown): void {
|
|
159
|
+
for (const m of Array.isArray(body) ? body : [body]) {
|
|
160
|
+
const msg = m as { method?: unknown; params?: { requestId?: unknown } };
|
|
161
|
+
const rid = msg?.params?.requestId;
|
|
162
|
+
if (msg?.method !== "notifications/cancelled") continue;
|
|
163
|
+
if (typeof rid !== "string" && typeof rid !== "number") continue;
|
|
164
|
+
const card = this.waiting.get(requestKey(rid));
|
|
165
|
+
if (card) this.pending.respond(card, { decision: "expired" });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
138
169
|
private allowed(req: http.IncomingMessage): number {
|
|
139
170
|
if (req.headers.origin) return 403;
|
|
140
171
|
const host = (req.headers.host ?? "").replace(/:\d+$/, "");
|
|
@@ -176,6 +207,7 @@ export class CopilotAskServer {
|
|
|
176
207
|
res.writeHead(400).end();
|
|
177
208
|
return;
|
|
178
209
|
}
|
|
210
|
+
this.cancelWaiting(body);
|
|
179
211
|
const server = this.build();
|
|
180
212
|
const transport = new StreamableHTTPServerTransport({
|
|
181
213
|
sessionIdGenerator: undefined,
|
|
@@ -222,6 +254,7 @@ export class CopilotAskServer {
|
|
|
222
254
|
// and stops listening.
|
|
223
255
|
close(): void {
|
|
224
256
|
this.pending.expireAll();
|
|
257
|
+
this.waiting.clear();
|
|
225
258
|
const server = this.http;
|
|
226
259
|
this.http = undefined;
|
|
227
260
|
server?.close();
|