@standardagents/code 0.13.3 → 0.13.4
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/dist/index.js +173 -180
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -5,9 +5,7 @@ import readline3 from 'readline/promises';
|
|
|
5
5
|
import { stdout, stdin } from 'process';
|
|
6
6
|
import * as api_star from '@standardagents/code-network/api';
|
|
7
7
|
import * as stream_star from '@standardagents/code-network/stream';
|
|
8
|
-
import
|
|
9
|
-
import * as heartbeat_star from '@standardagents/code-network/heartbeat';
|
|
10
|
-
import * as events_stream_star from '@standardagents/code-network/events-stream';
|
|
8
|
+
import * as hub_star from '@standardagents/code-network/hub';
|
|
11
9
|
import * as subagent_streams_star from '@standardagents/code-network/subagent-streams';
|
|
12
10
|
import * as session_state_star from '@standardagents/code-network/session-state';
|
|
13
11
|
import * as transcript_delivery_star from '@standardagents/code-network/transcript-delivery';
|
|
@@ -26,6 +24,7 @@ import { startDeviceLogin, pollDeviceLogin } from '@standardagents/code-network/
|
|
|
26
24
|
import * as recorder_star from '@standardagents/code-network/recorder';
|
|
27
25
|
import * as recorder_node_star from '@standardagents/code-network/recorder-node';
|
|
28
26
|
import { PRODUCTION_ENDPOINT, OPENSAMA_AGENT_ID, AGENT_ID, AGENT_ID_VARIANTS, AGENT_CHOICES } from '@standardagents/code-network/agents';
|
|
27
|
+
import crypto from 'crypto';
|
|
29
28
|
import { parseStopProcessArgs } from '@standardagents/code-network/registry';
|
|
30
29
|
import * as relay_star from '@standardagents/code-network/relay';
|
|
31
30
|
import fsp2 from 'fs/promises';
|
|
@@ -71,33 +70,37 @@ var themeGray = isLightTheme ? "\x1B[38;5;244m" : "\x1B[90m";
|
|
|
71
70
|
var stream_exports = {};
|
|
72
71
|
__reExport(stream_exports, stream_star);
|
|
73
72
|
|
|
74
|
-
// src/
|
|
75
|
-
var
|
|
76
|
-
__reExport(
|
|
73
|
+
// src/hub.ts
|
|
74
|
+
var hub_exports = {};
|
|
75
|
+
__reExport(hub_exports, hub_star);
|
|
77
76
|
|
|
78
77
|
// src/account-user-stream.ts
|
|
79
78
|
var AccountUserStream = class {
|
|
80
|
-
constructor(api, clientId, options) {
|
|
81
|
-
this.api = api;
|
|
82
|
-
this.clientId = clientId;
|
|
83
|
-
this.options = options;
|
|
84
|
-
}
|
|
85
|
-
api;
|
|
86
|
-
clientId;
|
|
87
|
-
options;
|
|
88
|
-
ws = null;
|
|
89
|
-
closed = false;
|
|
90
79
|
started = false;
|
|
91
|
-
|
|
92
|
-
reconnectAttempt = 0;
|
|
93
|
-
reconnectTimer = null;
|
|
80
|
+
hub;
|
|
94
81
|
eventListeners = /* @__PURE__ */ new Set();
|
|
95
82
|
connectionListeners = /* @__PURE__ */ new Set();
|
|
96
83
|
connected = false;
|
|
84
|
+
constructor(api, clientId, options) {
|
|
85
|
+
this.hub = new hub_exports.HubSocket(
|
|
86
|
+
api,
|
|
87
|
+
clientId,
|
|
88
|
+
{
|
|
89
|
+
onEvent: (event, data) => this.emitEvent(event, data),
|
|
90
|
+
onConnection: (state, attempt) => {
|
|
91
|
+
this.connected = state === "connected";
|
|
92
|
+
this.emitConnection(state, attempt);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
options.clientName,
|
|
96
|
+
options.clientKind,
|
|
97
|
+
options.uniqueConnectionId
|
|
98
|
+
);
|
|
99
|
+
}
|
|
97
100
|
start() {
|
|
98
|
-
if (this.started
|
|
101
|
+
if (this.started) return;
|
|
99
102
|
this.started = true;
|
|
100
|
-
this.
|
|
103
|
+
this.hub.start();
|
|
101
104
|
}
|
|
102
105
|
onEvent(listener) {
|
|
103
106
|
this.eventListeners.add(listener);
|
|
@@ -125,102 +128,65 @@ var AccountUserStream = class {
|
|
|
125
128
|
}, timeoutMs);
|
|
126
129
|
});
|
|
127
130
|
}
|
|
128
|
-
openSocket() {
|
|
129
|
-
if (this.closed) return;
|
|
130
|
-
const clientId = this.options.uniqueConnectionId ? `${this.clientId}:${crypto2.randomBytes(8).toString("hex")}` : this.clientId;
|
|
131
|
-
let url = `${this.api.wsEndpoint}/api/users/me/stream?token=${encodeURIComponent(this.api.bearer)}&client_id=${encodeURIComponent(clientId)}&client_kind=${encodeURIComponent(this.options.clientKind)}`;
|
|
132
|
-
if (this.options.clientName) url += `&client_name=${encodeURIComponent(this.options.clientName)}`;
|
|
133
|
-
let ws;
|
|
134
|
-
try {
|
|
135
|
-
ws = new WebSocket(url);
|
|
136
|
-
} catch {
|
|
137
|
-
this.scheduleReconnect();
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
this.ws = ws;
|
|
141
|
-
ws.addEventListener("open", () => {
|
|
142
|
-
if (this.ws !== ws) return;
|
|
143
|
-
this.connected = true;
|
|
144
|
-
this.reconnectAttempt = 0;
|
|
145
|
-
this.startHeartbeat(ws);
|
|
146
|
-
this.emitConnection("connected", 0);
|
|
147
|
-
});
|
|
148
|
-
ws.addEventListener("message", (event) => {
|
|
149
|
-
if (this.ws === ws) this.heartbeat?.markAlive();
|
|
150
|
-
this.onMessage(String(event.data));
|
|
151
|
-
});
|
|
152
|
-
ws.addEventListener("error", () => this.handleDrop(ws));
|
|
153
|
-
ws.addEventListener("close", () => this.handleDrop(ws));
|
|
154
|
-
}
|
|
155
|
-
onMessage(raw) {
|
|
156
|
-
let message;
|
|
157
|
-
try {
|
|
158
|
-
message = JSON.parse(raw);
|
|
159
|
-
} catch {
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
if (!message || typeof message !== "object") return;
|
|
163
|
-
const frame = message;
|
|
164
|
-
if (typeof frame.event === "string") {
|
|
165
|
-
this.emitEvent(frame.event, frame.data);
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
if (frame.type === "wake" && typeof frame.threadId === "string") {
|
|
169
|
-
this.emitEvent("standardagents.wake", { thread_id: frame.threadId });
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
131
|
emitEvent(event, data) {
|
|
173
132
|
for (const listener of this.eventListeners) listener(event, data);
|
|
174
133
|
}
|
|
175
134
|
emitConnection(state, attempt) {
|
|
176
135
|
for (const listener of this.connectionListeners) listener(state, attempt);
|
|
177
136
|
}
|
|
178
|
-
handleDrop(ws) {
|
|
179
|
-
if (this.ws !== ws) return;
|
|
180
|
-
this.ws = null;
|
|
181
|
-
this.connected = false;
|
|
182
|
-
this.stopHeartbeat();
|
|
183
|
-
this.scheduleReconnect();
|
|
184
|
-
}
|
|
185
|
-
scheduleReconnect() {
|
|
186
|
-
if (this.closed || this.reconnectTimer) return;
|
|
187
|
-
this.reconnectAttempt++;
|
|
188
|
-
this.emitConnection("reconnecting", this.reconnectAttempt);
|
|
189
|
-
const base = Math.min(500 * 2 ** (this.reconnectAttempt - 1), 15e3);
|
|
190
|
-
const delay = base + Math.floor(Math.random() * 400);
|
|
191
|
-
this.reconnectTimer = setTimeout(() => {
|
|
192
|
-
this.reconnectTimer = null;
|
|
193
|
-
this.openSocket();
|
|
194
|
-
}, delay);
|
|
195
|
-
}
|
|
196
|
-
startHeartbeat(ws) {
|
|
197
|
-
this.stopHeartbeat();
|
|
198
|
-
this.heartbeat = new heartbeat_exports.Heartbeat(ws, () => this.handleDrop(ws), { request: "stream_ping" });
|
|
199
|
-
this.heartbeat.start();
|
|
200
|
-
}
|
|
201
|
-
stopHeartbeat() {
|
|
202
|
-
this.heartbeat?.stop();
|
|
203
|
-
this.heartbeat = null;
|
|
204
|
-
}
|
|
205
137
|
close() {
|
|
206
|
-
this.closed = true;
|
|
207
138
|
this.connected = false;
|
|
208
|
-
this.
|
|
209
|
-
if (this.reconnectTimer) {
|
|
210
|
-
clearTimeout(this.reconnectTimer);
|
|
211
|
-
this.reconnectTimer = null;
|
|
212
|
-
}
|
|
213
|
-
const ws = this.ws;
|
|
214
|
-
this.ws = null;
|
|
215
|
-
ws?.close();
|
|
139
|
+
this.hub.close();
|
|
216
140
|
this.eventListeners.clear();
|
|
217
141
|
this.connectionListeners.clear();
|
|
218
142
|
}
|
|
219
143
|
};
|
|
220
144
|
|
|
221
|
-
// src/events
|
|
222
|
-
var
|
|
223
|
-
|
|
145
|
+
// packages/core/src/session-events.ts
|
|
146
|
+
var SESSION_CHANGED_EVENT = "standardcode.sessions_changed";
|
|
147
|
+
var SESSION_CHANGES = /* @__PURE__ */ new Set([
|
|
148
|
+
"created",
|
|
149
|
+
"updated",
|
|
150
|
+
"deleted",
|
|
151
|
+
"metadata",
|
|
152
|
+
"activity",
|
|
153
|
+
"subagents"
|
|
154
|
+
]);
|
|
155
|
+
function parseSessionChangedData(value) {
|
|
156
|
+
if (!value || typeof value !== "object") return null;
|
|
157
|
+
const raw = value;
|
|
158
|
+
if (typeof raw.change !== "string" || !SESSION_CHANGES.has(raw.change)) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const parsed = {
|
|
162
|
+
change: raw.change,
|
|
163
|
+
...typeof raw.thread_id === "string" && raw.thread_id ? { thread_id: raw.thread_id } : {}
|
|
164
|
+
};
|
|
165
|
+
if (Object.prototype.hasOwnProperty.call(raw, "name") && (raw.name === null || typeof raw.name === "string")) {
|
|
166
|
+
parsed.name = raw.name;
|
|
167
|
+
}
|
|
168
|
+
if (typeof raw.archived === "boolean") parsed.archived = raw.archived;
|
|
169
|
+
if (raw.thread && typeof raw.thread === "object" && !Array.isArray(raw.thread)) {
|
|
170
|
+
const thread = raw.thread;
|
|
171
|
+
const id = typeof thread.id === "string" && thread.id ? thread.id : parsed.thread_id;
|
|
172
|
+
if (id && (!parsed.thread_id || parsed.thread_id === id)) {
|
|
173
|
+
const delta = { id };
|
|
174
|
+
if (typeof thread.agent_id === "string") delta.agent_id = thread.agent_id;
|
|
175
|
+
if (Array.isArray(thread.tags)) {
|
|
176
|
+
delta.tags = thread.tags.filter((tag) => typeof tag === "string");
|
|
177
|
+
}
|
|
178
|
+
for (const key of ["created_at", "updated_at", "last_message_at"]) {
|
|
179
|
+
if (typeof thread[key] === "number" && Number.isFinite(thread[key])) delta[key] = thread[key];
|
|
180
|
+
}
|
|
181
|
+
for (const key of ["title", "preview"]) {
|
|
182
|
+
if (typeof thread[key] === "string") delta[key] = thread[key];
|
|
183
|
+
}
|
|
184
|
+
if (typeof thread.executing === "boolean") delta.executing = thread.executing;
|
|
185
|
+
parsed.thread = delta;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return parsed;
|
|
189
|
+
}
|
|
224
190
|
|
|
225
191
|
// src/subagent-streams.ts
|
|
226
192
|
var subagent_streams_exports = {};
|
|
@@ -3938,7 +3904,7 @@ function readVersion() {
|
|
|
3938
3904
|
if (typeof pkg.version === "string" && pkg.version) return pkg.version;
|
|
3939
3905
|
} catch {
|
|
3940
3906
|
}
|
|
3941
|
-
return "0.13.
|
|
3907
|
+
return "0.13.4" ;
|
|
3942
3908
|
}
|
|
3943
3909
|
function isLocalHost(host) {
|
|
3944
3910
|
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".local") || host.endsWith(".localhost") || /^10\./.test(host) || /^192\.168\./.test(host) || /^172\.(1[6-9]|2\d|3[01])\./.test(host);
|
|
@@ -3971,7 +3937,7 @@ function loadMachineIdentity() {
|
|
|
3971
3937
|
} catch {
|
|
3972
3938
|
}
|
|
3973
3939
|
const identity = {
|
|
3974
|
-
machine_id:
|
|
3940
|
+
machine_id: crypto.randomUUID(),
|
|
3975
3941
|
created_at: Date.now()
|
|
3976
3942
|
};
|
|
3977
3943
|
saveMachineIdentity(identity);
|
|
@@ -3985,7 +3951,7 @@ function daemonClientId(identity) {
|
|
|
3985
3951
|
return `daemon:${identity.machine_id}`;
|
|
3986
3952
|
}
|
|
3987
3953
|
function interactiveClientId(identity) {
|
|
3988
|
-
return `cli:${identity.machine_id}:${
|
|
3954
|
+
return `cli:${identity.machine_id}:${crypto.randomBytes(4).toString("hex")}`;
|
|
3989
3955
|
}
|
|
3990
3956
|
var KEY_PREFIX = "standardcode.machine.";
|
|
3991
3957
|
var CMD_SUFFIX = ".cmd";
|
|
@@ -4366,7 +4332,7 @@ async function enqueueMachineCommand(api, machineId, kind, args) {
|
|
|
4366
4332
|
const key = commandKey(machineId);
|
|
4367
4333
|
const queue = parseCommands(await api.userKvGet(key));
|
|
4368
4334
|
const cmd = {
|
|
4369
|
-
id:
|
|
4335
|
+
id: crypto.randomBytes(6).toString("hex"),
|
|
4370
4336
|
kind,
|
|
4371
4337
|
args,
|
|
4372
4338
|
requested_at: Date.now()
|
|
@@ -4604,7 +4570,7 @@ function parseBrowseResult(value) {
|
|
|
4604
4570
|
}
|
|
4605
4571
|
function remoteBrowseBackend(api, accountStream, machineId, label) {
|
|
4606
4572
|
const rpc = async (req) => {
|
|
4607
|
-
const nonce =
|
|
4573
|
+
const nonce = crypto.randomBytes(8).toString("hex");
|
|
4608
4574
|
const res = await requestFsBrowse(api, accountStream, machineId, { nonce, ...req }).catch(
|
|
4609
4575
|
(error) => {
|
|
4610
4576
|
throw new Error(
|
|
@@ -4821,7 +4787,7 @@ function download(url, dest, redirects = 5) {
|
|
|
4821
4787
|
});
|
|
4822
4788
|
}
|
|
4823
4789
|
async function sha256File(file2) {
|
|
4824
|
-
const hash =
|
|
4790
|
+
const hash = crypto.createHash("sha256");
|
|
4825
4791
|
await new Promise((resolve, reject) => {
|
|
4826
4792
|
const stream = fs11.createReadStream(file2);
|
|
4827
4793
|
stream.on("data", (chunk) => hash.update(chunk));
|
|
@@ -5133,7 +5099,7 @@ var HostTools = class {
|
|
|
5133
5099
|
return { ok: false, error: "args_json must be a JSON array of strings" };
|
|
5134
5100
|
}
|
|
5135
5101
|
}
|
|
5136
|
-
const hash =
|
|
5102
|
+
const hash = crypto.createHash("sha256").update(JSON.stringify(files)).digest("hex").slice(0, 12);
|
|
5137
5103
|
const skillDir = path9.join(os6.tmpdir(), "standardcode-skills", `${skill}-${hash}`);
|
|
5138
5104
|
for (const f of files) {
|
|
5139
5105
|
const dest = path9.resolve(skillDir, f.path);
|
|
@@ -5225,7 +5191,7 @@ ${truncated}`
|
|
|
5225
5191
|
if (!fs11.existsSync(cwd)) {
|
|
5226
5192
|
return { ok: false, error: `cwd does not exist: ${cwd}` };
|
|
5227
5193
|
}
|
|
5228
|
-
const id =
|
|
5194
|
+
const id = crypto.randomUUID().slice(0, 8);
|
|
5229
5195
|
const logPath = path9.join(LOG_DIR, `${id}.log`);
|
|
5230
5196
|
let out;
|
|
5231
5197
|
try {
|
|
@@ -5629,7 +5595,7 @@ ${stderrTail.trim()}` : msg;
|
|
|
5629
5595
|
target,
|
|
5630
5596
|
argsSha256: sha256(canonicalJson(args)),
|
|
5631
5597
|
resultSha256: sha256(text),
|
|
5632
|
-
nonce:
|
|
5598
|
+
nonce: crypto.randomBytes(8).toString("hex"),
|
|
5633
5599
|
isError,
|
|
5634
5600
|
at: Date.now()
|
|
5635
5601
|
};
|
|
@@ -5899,7 +5865,7 @@ function sortKeys(value) {
|
|
|
5899
5865
|
return value;
|
|
5900
5866
|
}
|
|
5901
5867
|
function sha256(input3) {
|
|
5902
|
-
return
|
|
5868
|
+
return crypto.createHash("sha256").update(input3).digest("hex");
|
|
5903
5869
|
}
|
|
5904
5870
|
|
|
5905
5871
|
// src/execution-session.ts
|
|
@@ -6021,6 +5987,27 @@ var DaemonUserStream = class {
|
|
|
6021
5987
|
this.stream.close();
|
|
6022
5988
|
}
|
|
6023
5989
|
};
|
|
5990
|
+
|
|
5991
|
+
// src/daemon-activity.ts
|
|
5992
|
+
var DaemonWorkerActivity = class {
|
|
5993
|
+
activeTools = /* @__PURE__ */ new Set();
|
|
5994
|
+
approvalLeases = /* @__PURE__ */ new Set();
|
|
5995
|
+
setToolActive(toolCallId, active) {
|
|
5996
|
+
if (active) this.activeTools.add(toolCallId);
|
|
5997
|
+
else this.activeTools.delete(toolCallId);
|
|
5998
|
+
}
|
|
5999
|
+
/** Hold the worker active until the returned idempotent release runs. */
|
|
6000
|
+
beginApproval() {
|
|
6001
|
+
const lease = /* @__PURE__ */ Symbol("approval");
|
|
6002
|
+
this.approvalLeases.add(lease);
|
|
6003
|
+
return () => {
|
|
6004
|
+
this.approvalLeases.delete(lease);
|
|
6005
|
+
};
|
|
6006
|
+
}
|
|
6007
|
+
get busy() {
|
|
6008
|
+
return this.activeTools.size > 0 || this.approvalLeases.size > 0;
|
|
6009
|
+
}
|
|
6010
|
+
};
|
|
6024
6011
|
var PKG_NAME = "@standardagents/code";
|
|
6025
6012
|
var REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(PKG_NAME)}`;
|
|
6026
6013
|
var CACHE_REL_DIR = ".config/standardagents-cli";
|
|
@@ -6199,19 +6186,7 @@ function runUpdate(pm) {
|
|
|
6199
6186
|
}
|
|
6200
6187
|
|
|
6201
6188
|
// src/daemon.ts
|
|
6202
|
-
var approvalWakes =
|
|
6203
|
-
function subscribeApprovalWake(threadId, wake) {
|
|
6204
|
-
const set = approvalWakes.get(threadId) ?? /* @__PURE__ */ new Set();
|
|
6205
|
-
set.add(wake);
|
|
6206
|
-
approvalWakes.set(threadId, set);
|
|
6207
|
-
return () => {
|
|
6208
|
-
set.delete(wake);
|
|
6209
|
-
if (set.size === 0) approvalWakes.delete(threadId);
|
|
6210
|
-
};
|
|
6211
|
-
}
|
|
6212
|
-
function wakeApprovals(threadId) {
|
|
6213
|
-
for (const wake of approvalWakes.get(threadId) ?? []) wake();
|
|
6214
|
-
}
|
|
6189
|
+
var approvalWakes = new relay_exports.ApprovalWakeRegistry();
|
|
6215
6190
|
var UPDATE_CHECK_MS = 6 * 60 * 6e4;
|
|
6216
6191
|
var MAX_WORKERS = 30;
|
|
6217
6192
|
var BRIDGE_ATTACH_GRACE_MS = 15e3;
|
|
@@ -6258,11 +6233,11 @@ var ThreadWorker = class {
|
|
|
6258
6233
|
},
|
|
6259
6234
|
hooks: {
|
|
6260
6235
|
onActivity: (line) => daemonLog(`[${threadId.slice(0, 8)}] ${line}`),
|
|
6261
|
-
onStatus: (
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
if (
|
|
6265
|
-
else if (
|
|
6236
|
+
onStatus: (id, summary) => {
|
|
6237
|
+
const wasBusy = this.busy;
|
|
6238
|
+
this.activity.setToolActive(id, summary !== null);
|
|
6239
|
+
if (this.busy) this.clearIdleTimer();
|
|
6240
|
+
else if (wasBusy) this.scheduleIdle(BRIDGE_IDLE_MS);
|
|
6266
6241
|
},
|
|
6267
6242
|
onConnection: (state, attempt) => {
|
|
6268
6243
|
if (state !== "reconnecting" || attempt === 1 || attempt % 10 === 0) {
|
|
@@ -6300,29 +6275,36 @@ var ThreadWorker = class {
|
|
|
6300
6275
|
return { choice: "deny", reason: "No one is available to approve this right now." };
|
|
6301
6276
|
}
|
|
6302
6277
|
daemonLog(`[${threadId.slice(0, 8)}] relaying approval: ${summary} (risk ${effectiveRisk})`);
|
|
6303
|
-
const
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
(
|
|
6278
|
+
const releaseApproval = this.activity.beginApproval();
|
|
6279
|
+
this.clearIdleTimer();
|
|
6280
|
+
try {
|
|
6281
|
+
const response = await (0, relay_exports.awaitApprovalViaRelay)(
|
|
6282
|
+
api,
|
|
6283
|
+
threadId,
|
|
6284
|
+
{
|
|
6285
|
+
tool_call_id: req.toolCallId,
|
|
6286
|
+
tool: req.tool,
|
|
6287
|
+
summary,
|
|
6288
|
+
permission: req.requestPermission,
|
|
6289
|
+
risk: effectiveRisk,
|
|
6290
|
+
machine: machineName,
|
|
6291
|
+
requested_at: Date.now()
|
|
6292
|
+
},
|
|
6293
|
+
{ subscribe: (wake) => approvalWakes.subscribe(threadId, wake) }
|
|
6294
|
+
);
|
|
6295
|
+
if (!response) {
|
|
6296
|
+
return { choice: "deny", reason: "The approval request timed out with no one to approve it." };
|
|
6297
|
+
}
|
|
6298
|
+
if (response.choice === "always") this.perm.alwaysAllow.add(permKey);
|
|
6299
|
+
if (response.choice === "always_risk") this.perm.allowRisk.add(effectiveRisk);
|
|
6300
|
+
if (response.choice === "always" || response.choice === "always_risk") {
|
|
6301
|
+
(0, approvals_exports.saveApprovals)(api, threadId, this.perm);
|
|
6302
|
+
}
|
|
6303
|
+
return { choice: response.choice, reason: response.reason };
|
|
6304
|
+
} finally {
|
|
6305
|
+
releaseApproval();
|
|
6306
|
+
if (!this.busy) this.scheduleIdle(BRIDGE_IDLE_MS);
|
|
6324
6307
|
}
|
|
6325
|
-
return { choice: response.choice, reason: response.reason };
|
|
6326
6308
|
}
|
|
6327
6309
|
}
|
|
6328
6310
|
});
|
|
@@ -6337,7 +6319,7 @@ var ThreadWorker = class {
|
|
|
6337
6319
|
createdAt;
|
|
6338
6320
|
session;
|
|
6339
6321
|
perm;
|
|
6340
|
-
|
|
6322
|
+
activity = new DaemonWorkerActivity();
|
|
6341
6323
|
idleTimer = null;
|
|
6342
6324
|
watchedAnotherOwner = false;
|
|
6343
6325
|
/** Set by the daemon so a superseded worker can remove itself. */
|
|
@@ -6345,7 +6327,7 @@ var ThreadWorker = class {
|
|
|
6345
6327
|
/** Set by the daemon so an inactive bridge removes itself. */
|
|
6346
6328
|
onIdle;
|
|
6347
6329
|
get busy() {
|
|
6348
|
-
return this.
|
|
6330
|
+
return this.activity.busy;
|
|
6349
6331
|
}
|
|
6350
6332
|
clearIdleTimer() {
|
|
6351
6333
|
if (!this.idleTimer) return;
|
|
@@ -6714,7 +6696,7 @@ Run \`standardcode daemon install\` (or plain \`standardcode\`) once to sign in.
|
|
|
6714
6696
|
onEvent: (event, data) => {
|
|
6715
6697
|
if (event === "standardcode.approval_changed") {
|
|
6716
6698
|
const threadId = data && typeof data === "object" ? data.thread_id : null;
|
|
6717
|
-
if (typeof threadId === "string")
|
|
6699
|
+
if (typeof threadId === "string") approvalWakes.wake(threadId);
|
|
6718
6700
|
return;
|
|
6719
6701
|
}
|
|
6720
6702
|
if (event === "standardcode.lines_changed") {
|
|
@@ -6734,16 +6716,21 @@ Run \`standardcode daemon install\` (or plain \`standardcode\`) once to sign in.
|
|
|
6734
6716
|
return;
|
|
6735
6717
|
}
|
|
6736
6718
|
if (event !== "standardcode.machine_changed") return;
|
|
6737
|
-
const
|
|
6719
|
+
const frame = data && typeof data === "object" ? data : null;
|
|
6720
|
+
const key = frame?.key;
|
|
6738
6721
|
daemonLog(`machine event: ${typeof key === "string" ? key : "missing key"}`);
|
|
6739
6722
|
if (key === `${machinePrefix}.cmd`) void drainCommands();
|
|
6740
6723
|
else if (key === `${machinePrefix}.fsreq`) {
|
|
6741
6724
|
void drainFsRequests();
|
|
6742
6725
|
} else if (key === `${machinePrefix}.name`) {
|
|
6743
|
-
|
|
6744
|
-
displayName =
|
|
6745
|
-
}
|
|
6746
|
-
|
|
6726
|
+
if (frame && Object.prototype.hasOwnProperty.call(frame, "value")) {
|
|
6727
|
+
displayName = typeof frame.value === "string" && frame.value.trim() ? frame.value.trim() : os6.hostname();
|
|
6728
|
+
} else {
|
|
6729
|
+
void getMachineName(api, identity.machine_id).then((name) => {
|
|
6730
|
+
displayName = name || os6.hostname();
|
|
6731
|
+
}).catch(() => {
|
|
6732
|
+
});
|
|
6733
|
+
}
|
|
6747
6734
|
}
|
|
6748
6735
|
},
|
|
6749
6736
|
onConnection: (state, attempt) => {
|
|
@@ -6755,6 +6742,7 @@ Run \`standardcode daemon install\` (or plain \`standardcode\`) once to sign in.
|
|
|
6755
6742
|
return;
|
|
6756
6743
|
}
|
|
6757
6744
|
daemonLog("user stream: connected");
|
|
6745
|
+
approvalWakes.wakeAll();
|
|
6758
6746
|
void publishPresence(true).then(() => recoverOnConnect());
|
|
6759
6747
|
}
|
|
6760
6748
|
},
|
|
@@ -7153,6 +7141,11 @@ async function runDaemonCommand(argv) {
|
|
|
7153
7141
|
const { endpoint, rest } = parseEndpointFlag(restArgs);
|
|
7154
7142
|
switch (command) {
|
|
7155
7143
|
case "run":
|
|
7144
|
+
if (rest.includes("--ephemeral") || process.env.STANDARD_CODE_EPHEMERAL === "1") {
|
|
7145
|
+
process.stdin.resume();
|
|
7146
|
+
process.stdin.on("end", () => process.exit(0));
|
|
7147
|
+
process.stdin.on("close", () => process.exit(0));
|
|
7148
|
+
}
|
|
7156
7149
|
await runDaemon({ endpoint: endpoint || process.env.STANDARD_CODE_DAEMON_ENDPOINT });
|
|
7157
7150
|
return;
|
|
7158
7151
|
case "install":
|
|
@@ -7885,6 +7878,14 @@ Run \`standardcode daemon install\` any time, or pick another machine.${c5.reset
|
|
|
7885
7878
|
void enqueueMachineCommand(api, session.runner.id, "add_project", { path: runnerPath }).catch(() => {
|
|
7886
7879
|
});
|
|
7887
7880
|
}
|
|
7881
|
+
const createdAt = Math.floor(Date.now() / 1e3);
|
|
7882
|
+
await api.notifySessionsChanged(id, "created", {
|
|
7883
|
+
id,
|
|
7884
|
+
agent_id: selectedAgent,
|
|
7885
|
+
tags,
|
|
7886
|
+
created_at: createdAt,
|
|
7887
|
+
updated_at: createdAt
|
|
7888
|
+
});
|
|
7888
7889
|
return id;
|
|
7889
7890
|
};
|
|
7890
7891
|
let threadId = "";
|
|
@@ -8151,6 +8152,7 @@ async function runAgentSwitchMenu(tui, api, threadId) {
|
|
|
8151
8152
|
const title = AGENT_CHOICES.find((choice) => choice.id === picked)?.title ?? picked;
|
|
8152
8153
|
try {
|
|
8153
8154
|
await api.setThreadAgent(threadId, picked);
|
|
8155
|
+
await api.notifySessionsChanged(threadId, "updated", { id: threadId, agent_id: picked });
|
|
8154
8156
|
tui.setAgentLabel(title);
|
|
8155
8157
|
tui.print(`${c5.green}\u2713${c5.reset} Session handed to ${c5.bold}${title}${c5.reset} \u2014 applies from your next message.`);
|
|
8156
8158
|
} catch (e) {
|
|
@@ -8381,15 +8383,12 @@ async function runInteractive(tui, api, accountStream, threadId, projectDir, mac
|
|
|
8381
8383
|
if (eventType === "generation" && typeof data?.outputTokens === "number") {
|
|
8382
8384
|
liveOut = data.outputTokens;
|
|
8383
8385
|
refreshStatus();
|
|
8384
|
-
scheduleProjectionRefresh();
|
|
8385
8386
|
} else if (eventType === "tool_call_started" && data?.id) {
|
|
8386
8387
|
activeSteps.set(data.id, data.progress || data.name || "working");
|
|
8387
8388
|
refreshStatus();
|
|
8388
|
-
scheduleProjectionRefresh();
|
|
8389
8389
|
} else if (eventType === "tool_call_done" && data?.id) {
|
|
8390
8390
|
activeSteps.delete(data.id);
|
|
8391
8391
|
refreshStatus();
|
|
8392
|
-
scheduleProjectionRefresh();
|
|
8393
8392
|
} else if (eventType === "goal_updated" && data) {
|
|
8394
8393
|
tui.setGoal(data);
|
|
8395
8394
|
} else if (eventType === "standardcode.approval_changed") {
|
|
@@ -8453,25 +8452,18 @@ async function runInteractive(tui, api, accountStream, threadId, projectDir, mac
|
|
|
8453
8452
|
}
|
|
8454
8453
|
}, 150);
|
|
8455
8454
|
};
|
|
8456
|
-
const
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
},
|
|
8461
|
-
onThreadUpdated: (t) => {
|
|
8462
|
-
if (t.parent === threadId) scheduleReconcile();
|
|
8463
|
-
},
|
|
8464
|
-
onThreadDeleted: (id) => {
|
|
8465
|
-
if (activeSubagents.has(id)) scheduleReconcile();
|
|
8466
|
-
}
|
|
8455
|
+
const stopSessionEvents = accountStream.onEvent((event, data) => {
|
|
8456
|
+
if (event !== SESSION_CHANGED_EVENT) return;
|
|
8457
|
+
const changed = parseSessionChangedData(data);
|
|
8458
|
+
if (changed?.thread_id === threadId && changed.change === "subagents") scheduleReconcile();
|
|
8467
8459
|
});
|
|
8468
8460
|
let endSession;
|
|
8469
8461
|
const sessionEnded = new Promise((r) => endSession = r);
|
|
8470
8462
|
const quit = async () => {
|
|
8471
8463
|
tui.end();
|
|
8472
8464
|
stopWatchingConnection();
|
|
8465
|
+
stopSessionEvents();
|
|
8473
8466
|
stream.close();
|
|
8474
|
-
events.close();
|
|
8475
8467
|
subActivity.closeAll();
|
|
8476
8468
|
farewell(busy ? runnerName : void 0);
|
|
8477
8469
|
process.exit(0);
|
|
@@ -9053,16 +9045,17 @@ ${c5.gray}Close another session (its slot frees within ~90s), then resend your m
|
|
|
9053
9045
|
if (attempt >= 4) tui.setConnected(false);
|
|
9054
9046
|
} else {
|
|
9055
9047
|
tui.setConnected(true);
|
|
9048
|
+
scheduleReconcile();
|
|
9056
9049
|
}
|
|
9057
9050
|
});
|
|
9058
|
-
|
|
9051
|
+
scheduleReconcile();
|
|
9059
9052
|
await stream.connect();
|
|
9060
9053
|
void api.getGoal(threadId).then((g) => tui.setGoal(g)).catch(() => {
|
|
9061
9054
|
});
|
|
9062
9055
|
attaching.stop();
|
|
9063
9056
|
const header = `${c5.bold}${c5.magenta}Standard Code${c5.reset} ${c5.dim}\u2014 ${agentTitle}${c5.reset}`;
|
|
9064
9057
|
const daemonVersion = remote ? session.runner?.daemon?.version : (await loadMachine(api, session.identity.machine_id).catch(() => null))?.daemon?.version;
|
|
9065
|
-
const execLine = `${c5.gray}tool execution:${c5.reset} daemon${daemonVersion ? ` v${daemonVersion}` : ""} on ${runnerName}
|
|
9058
|
+
const execLine = `${c5.gray}tool execution:${c5.reset} daemon${daemonVersion ? ` v${daemonVersion}` : ""} on ${runnerName}`;
|
|
9066
9059
|
tui.setAgentLabel(agentTitle);
|
|
9067
9060
|
tui.banner(
|
|
9068
9061
|
remote ? [
|
|
@@ -9252,8 +9245,8 @@ ${c5.dim}runs on ${request.machine || runnerName}${c5.reset}`,
|
|
|
9252
9245
|
const stopped = api.stopThread(threadId).catch(() => {
|
|
9253
9246
|
});
|
|
9254
9247
|
stopWatchingConnection();
|
|
9248
|
+
stopSessionEvents();
|
|
9255
9249
|
stream.close();
|
|
9256
|
-
events.close();
|
|
9257
9250
|
subActivity.closeAll();
|
|
9258
9251
|
await Promise.race([stopped, new Promise((r) => setTimeout(r, 1500))]);
|
|
9259
9252
|
tui.setWorking(false);
|