@alook/cli 0.0.118 → 0.0.120
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 +211 -57
- package/dist/session-runner.js +54 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14561,7 +14561,8 @@ var DaemonPushMessageSchema = exports_external.discriminatedUnion("type", [
|
|
|
14561
14561
|
exports_external.object({ type: exports_external.literal("daemon.evict"), workspaceId: exports_external.string() }),
|
|
14562
14562
|
exports_external.object({ type: exports_external.literal("daemon.update"), version: exports_external.string() }),
|
|
14563
14563
|
exports_external.object({ type: exports_external.literal("daemon.rescan") }),
|
|
14564
|
-
exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), agentId: exports_external.string().min(1), taskId: exports_external.string(), targetTaskId: exports_external.string() })
|
|
14564
|
+
exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), agentId: exports_external.string().min(1), taskId: exports_external.string(), targetTaskId: exports_external.string() }),
|
|
14565
|
+
exports_external.object({ type: exports_external.literal("daemon.workspace_added"), workspaceId: exports_external.string(), workspaceName: exports_external.string(), token: exports_external.string() })
|
|
14565
14566
|
]);
|
|
14566
14567
|
var RegisterResponseSchema = exports_external.object({
|
|
14567
14568
|
runtimes: exports_external.array(exports_external.object({ id: exports_external.string() }))
|
|
@@ -14591,6 +14592,9 @@ var RegisterDaemonRequestSchema = exports_external.object({
|
|
|
14591
14592
|
workspaces_root: exports_external.string().optional().default(""),
|
|
14592
14593
|
runtimes: exports_external.array(DaemonRuntimeItemSchema).min(1)
|
|
14593
14594
|
});
|
|
14595
|
+
var BindWorkspaceRequestSchema = exports_external.object({
|
|
14596
|
+
workspace_id: exports_external.string().min(1)
|
|
14597
|
+
});
|
|
14594
14598
|
var DeregisterRequestSchema = exports_external.object({
|
|
14595
14599
|
daemon_id: exports_external.string().min(1)
|
|
14596
14600
|
});
|
|
@@ -14752,7 +14756,8 @@ var CreateConversationRequestSchema = exports_external.object({
|
|
|
14752
14756
|
channel: exports_external.string().optional()
|
|
14753
14757
|
});
|
|
14754
14758
|
var CreateMessageRequestSchema = exports_external.object({
|
|
14755
|
-
content: exports_external.string().min(1, "content is required")
|
|
14759
|
+
content: exports_external.string().min(1, "content is required"),
|
|
14760
|
+
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
14756
14761
|
});
|
|
14757
14762
|
var AgentDmRequestSchema = exports_external.object({
|
|
14758
14763
|
content: exports_external.string().min(1, "content is required"),
|
|
@@ -16735,6 +16740,8 @@ var machineToken = sqliteTable("machine_token", {
|
|
|
16735
16740
|
token: text("token").unique().notNull(),
|
|
16736
16741
|
name: text("name").notNull().default(""),
|
|
16737
16742
|
status: text("status").notNull().default("active"),
|
|
16743
|
+
hostname: text("hostname"),
|
|
16744
|
+
runtimesJson: text("runtimes_json"),
|
|
16738
16745
|
lastUsedAt: text("last_used_at"),
|
|
16739
16746
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
16740
16747
|
}, (t) => [index("idx_machine_token").on(t.token)]);
|
|
@@ -16883,7 +16890,24 @@ function isLocalUrl(url2) {
|
|
|
16883
16890
|
return false;
|
|
16884
16891
|
}
|
|
16885
16892
|
}
|
|
16893
|
+
function hasWindow() {
|
|
16894
|
+
return typeof globalThis !== "undefined" && "window" in globalThis;
|
|
16895
|
+
}
|
|
16896
|
+
function isTauri() {
|
|
16897
|
+
return hasWindow() && typeof window !== "undefined" && "__TAURI__" in window;
|
|
16898
|
+
}
|
|
16899
|
+
function isMobile() {
|
|
16900
|
+
if (!isTauri())
|
|
16901
|
+
return false;
|
|
16902
|
+
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
|
|
16903
|
+
return /(android|iphone|ipad|ipod)/i.test(ua);
|
|
16904
|
+
}
|
|
16886
16905
|
function resolveMode(signals) {
|
|
16906
|
+
if (signals.tauri || isTauri()) {
|
|
16907
|
+
if (signals.tauriPlatform === "mobile" || isMobile())
|
|
16908
|
+
return "mobile";
|
|
16909
|
+
return "desktop";
|
|
16910
|
+
}
|
|
16887
16911
|
if (signals.nodeEnv === "development" && !signals.cmdPrefix)
|
|
16888
16912
|
return "dev";
|
|
16889
16913
|
if (signals.serverUrl && !signals.cmdPrefix && signals.nodeEnv !== "production" && isLocalUrl(signals.serverUrl))
|
|
@@ -16900,6 +16924,8 @@ function cliCommand(mode) {
|
|
|
16900
16924
|
return "pnpm dev:cli";
|
|
16901
16925
|
case "app":
|
|
16902
16926
|
return "npx @alook/app cli";
|
|
16927
|
+
case "desktop":
|
|
16928
|
+
case "mobile":
|
|
16903
16929
|
case "production":
|
|
16904
16930
|
return "npx @alook/cli";
|
|
16905
16931
|
}
|
|
@@ -16958,10 +16984,19 @@ function loadCLIConfigForProfile(profile) {
|
|
|
16958
16984
|
if (profileName && cfg.profiles?.[profileName]) {
|
|
16959
16985
|
return cfg.profiles[profileName];
|
|
16960
16986
|
}
|
|
16961
|
-
|
|
16987
|
+
const result = {
|
|
16962
16988
|
server_url: cfg.server_url || "",
|
|
16963
16989
|
watched_workspaces: cfg.watched_workspaces || []
|
|
16964
16990
|
};
|
|
16991
|
+
const legacy = cfg.machine_token;
|
|
16992
|
+
if (legacy && !result.watched_workspaces.some((w) => w.token === legacy)) {
|
|
16993
|
+
result.watched_workspaces.push({ id: null, name: null, token: legacy, status: "registered", agent_ids: [] });
|
|
16994
|
+
}
|
|
16995
|
+
for (const ws of result.watched_workspaces) {
|
|
16996
|
+
if (!ws.status)
|
|
16997
|
+
ws.status = ws.id ? "active" : "registered";
|
|
16998
|
+
}
|
|
16999
|
+
return result;
|
|
16965
17000
|
}
|
|
16966
17001
|
function saveCLIConfig(cfg) {
|
|
16967
17002
|
mkdirSync(configDir(), { recursive: true, mode: 448 });
|
|
@@ -16976,6 +17011,7 @@ function saveCLIConfigForProfile(profile, profileConfig) {
|
|
|
16976
17011
|
} else {
|
|
16977
17012
|
cfg.server_url = profileConfig.server_url;
|
|
16978
17013
|
cfg.watched_workspaces = profileConfig.watched_workspaces;
|
|
17014
|
+
delete cfg.machine_token;
|
|
16979
17015
|
}
|
|
16980
17016
|
saveCLIConfig(cfg);
|
|
16981
17017
|
}
|
|
@@ -17296,7 +17332,7 @@ async function activateAndSave(opts) {
|
|
|
17296
17332
|
}
|
|
17297
17333
|
console.log(`Found: ${runtimes.map((r) => r.type).join(", ")}`);
|
|
17298
17334
|
const host = hostname4();
|
|
17299
|
-
console.log("Registering
|
|
17335
|
+
console.log("Registering machine...");
|
|
17300
17336
|
let activateResp;
|
|
17301
17337
|
try {
|
|
17302
17338
|
const res = await fetch(`${serverUrl}/api/machine-tokens/activate`, {
|
|
@@ -17314,32 +17350,10 @@ async function activateAndSave(opts) {
|
|
|
17314
17350
|
console.error(`Error: failed to activate: ${err instanceof Error ? err.message : err}`);
|
|
17315
17351
|
process.exit(1);
|
|
17316
17352
|
}
|
|
17317
|
-
const client = new APIClient(serverUrl, token);
|
|
17318
|
-
let workspaces;
|
|
17319
|
-
try {
|
|
17320
|
-
workspaces = await client.getJSON("/api/workspaces");
|
|
17321
|
-
} catch (err) {
|
|
17322
|
-
console.error(`Error: failed to fetch workspaces: ${err instanceof Error ? err.message : err}`);
|
|
17323
|
-
process.exit(1);
|
|
17324
|
-
}
|
|
17325
|
-
if (!workspaces.length) {
|
|
17326
|
-
console.error("Error: no workspaces found for this user");
|
|
17327
|
-
process.exit(1);
|
|
17328
|
-
}
|
|
17329
|
-
const ws = workspaces.find((w) => w.id === activateResp.workspace_id) || workspaces[0];
|
|
17330
|
-
const wsClient = new APIClient(serverUrl, token, ws.id);
|
|
17331
|
-
let agentIds = [];
|
|
17332
|
-
try {
|
|
17333
|
-
const agents = await wsClient.getJSON(`/api/agents?workspace_id=${ws.id}`);
|
|
17334
|
-
agentIds = agents.map((a) => a.id);
|
|
17335
|
-
} catch {}
|
|
17336
17353
|
const existing = loadCLIConfigForProfile(profile);
|
|
17337
17354
|
const watched = existing.watched_workspaces || [];
|
|
17338
|
-
|
|
17339
|
-
|
|
17340
|
-
watched[idx] = { id: ws.id, name: ws.name, token, agent_ids: agentIds };
|
|
17341
|
-
} else {
|
|
17342
|
-
watched.push({ id: ws.id, name: ws.name, token, agent_ids: agentIds });
|
|
17355
|
+
if (!watched.some((w) => w.token === token)) {
|
|
17356
|
+
watched.push({ id: null, name: null, token, status: "registered", agent_ids: [] });
|
|
17343
17357
|
}
|
|
17344
17358
|
saveCLIConfigForProfile(profile, {
|
|
17345
17359
|
server_url: serverUrl,
|
|
@@ -17350,10 +17364,10 @@ async function activateAndSave(opts) {
|
|
|
17350
17364
|
try {
|
|
17351
17365
|
process.kill(daemonPid, "SIGHUP");
|
|
17352
17366
|
console.log(`
|
|
17353
|
-
Daemon (pid ${daemonPid}) notified —
|
|
17367
|
+
Daemon (pid ${daemonPid}) notified — machine registered, awaiting workspace binding.`);
|
|
17354
17368
|
} catch {
|
|
17355
17369
|
console.log(`
|
|
17356
|
-
Daemon is running but could not be notified. Restart it to pick up the new
|
|
17370
|
+
Daemon is running but could not be notified. Restart it to pick up the new token.`);
|
|
17357
17371
|
}
|
|
17358
17372
|
} else {
|
|
17359
17373
|
const startCmd = isDev() ? `${cmdPrefix()} daemon start --foreground` : `${cmdPrefix()} daemon start`;
|
|
@@ -17361,9 +17375,8 @@ Daemon is running but could not be notified. Restart it to pick up the new works
|
|
|
17361
17375
|
console.log(`Run '${startCmd}' to start the daemon.`);
|
|
17362
17376
|
}
|
|
17363
17377
|
return {
|
|
17364
|
-
|
|
17365
|
-
|
|
17366
|
-
runtimeProviders: activateResp.runtimes.map((r) => r.provider)
|
|
17378
|
+
daemonId: activateResp.daemon_id,
|
|
17379
|
+
tokenStatus: activateResp.token_status
|
|
17367
17380
|
};
|
|
17368
17381
|
}
|
|
17369
17382
|
|
|
@@ -17393,8 +17406,8 @@ Usage: ${cmdPrefix()} register --token <token>`);
|
|
|
17393
17406
|
const result = await activateAndSave({ token, serverUrl, profile });
|
|
17394
17407
|
console.log(`
|
|
17395
17408
|
Registered as ${me.email}`);
|
|
17396
|
-
console.log(`
|
|
17397
|
-
console.log(`
|
|
17409
|
+
console.log(`Machine: ${result.daemonId} (status: ${result.tokenStatus})`);
|
|
17410
|
+
console.log(`Workspace binding will happen when you launch a company.`);
|
|
17398
17411
|
});
|
|
17399
17412
|
return cmd;
|
|
17400
17413
|
}
|
|
@@ -17486,8 +17499,8 @@ async function pollAndActivate(opts) {
|
|
|
17486
17499
|
console.log(`
|
|
17487
17500
|
Logged in as ${email3}`);
|
|
17488
17501
|
}
|
|
17489
|
-
console.log(`
|
|
17490
|
-
console.log(`
|
|
17502
|
+
console.log(`Machine: ${result.daemonId} (status: ${result.tokenStatus})`);
|
|
17503
|
+
console.log(`Workspace binding will happen when you launch a company.`);
|
|
17491
17504
|
}
|
|
17492
17505
|
if (process.argv.includes("--__login-poll")) {
|
|
17493
17506
|
const idx = process.argv.indexOf("--__login-poll");
|
|
@@ -17527,7 +17540,7 @@ async function checkExistingAuth(serverUrl, profile) {
|
|
|
17527
17540
|
email3 = me.email;
|
|
17528
17541
|
}
|
|
17529
17542
|
} catch {}
|
|
17530
|
-
return { valid: true, email: email3, workspaceName: ws.name };
|
|
17543
|
+
return { valid: true, email: email3, workspaceName: ws.name ?? undefined };
|
|
17531
17544
|
} catch {
|
|
17532
17545
|
return { valid: false };
|
|
17533
17546
|
}
|
|
@@ -17752,6 +17765,9 @@ class DaemonClient {
|
|
|
17752
17765
|
syncSkills(token, body) {
|
|
17753
17766
|
return this.request("POST", "/api/daemon/skills/sync", token, body);
|
|
17754
17767
|
}
|
|
17768
|
+
async checkStandby(token, body) {
|
|
17769
|
+
return this.request("POST", "/api/daemon/register", token, body);
|
|
17770
|
+
}
|
|
17755
17771
|
}
|
|
17756
17772
|
|
|
17757
17773
|
// daemon/health.ts
|
|
@@ -19091,6 +19107,7 @@ The CLI auto-detects your identity from the environment. No need to pass \`--age
|
|
|
19091
19107
|
### Command quick reference
|
|
19092
19108
|
| Capability | Command |
|
|
19093
19109
|
|---|---|
|
|
19110
|
+
| Send a message to the user | \`${cmdPrefix()} sync send-dm\` |
|
|
19094
19111
|
| Schedule / list / edit tasks | \`${cmdPrefix()} calendar set\` (also list, show, update, delete) |
|
|
19095
19112
|
| Upload a file for your owner | \`${cmdPrefix()} sync upload-artifact\` |
|
|
19096
19113
|
| Recruit a colleague agent | \`${cmdPrefix()} agent recruit\` |
|
|
@@ -19181,11 +19198,24 @@ Upload files for your owner to review in the app.
|
|
|
19181
19198
|
### Talking to the user
|
|
19182
19199
|
You're texting a colleague, not filing a report. The only thing the user sees is what you send with \`${cmdPrefix()} sync send-dm\` — your task output, reasoning, and tool calls are all off-screen. If you finish without sending, they got silence.
|
|
19183
19200
|
|
|
19201
|
+
\`${cmdPrefix()} sync send-dm\` sends a message to **the user** (your owner), not to a colleague agent. This is how you communicate with the human who gave you the task. Use email to talk to colleague agents.
|
|
19202
|
+
|
|
19184
19203
|
Message at milestones, the way a person would: acknowledge when you pick something up, share a real step forward or a fork in the road, and deliver the result. A quick task is often one message; a long one is a few well-spaced check-ins. Trust your read of the moment — don't narrate every small step, and don't go dark for a long stretch on something they're waiting on.
|
|
19185
19204
|
|
|
19186
19205
|
Say what a colleague would say, not a transcript — the answer in your own voice. (Email- and calendar-triggered tasks have no one watching the chat; use email there.)
|
|
19187
19206
|
|
|
19188
|
-
|
|
19207
|
+
**A real person is waiting on the other end.** Send updates at every milestone of your work — not just the final result. For any task longer than a minute:
|
|
19208
|
+
1. **Before you start**: tell them your plan ("I'll research X, then modify Y and Z")
|
|
19209
|
+
2. **During work**: update when you find something important, change direction, or hit a blocker ("Found the issue — it's in the auth module, fixing now")
|
|
19210
|
+
3. **When done**: deliver the clear result
|
|
19211
|
+
|
|
19212
|
+
Don't bundle everything into one giant message at the end. The user shouldn't have to sit in silence wondering what's happening. A one-line progress update costs nothing and keeps the human in the loop. But don't send repetitive or near-identical messages — each update should carry new information, not just restate what you already said.
|
|
19213
|
+
|
|
19214
|
+
**If the user sends you a message while you're working** — especially questions like "are you there?", "what's the status?", or unrelated requests — **respond to them immediately**. Don't finish your current task first and then reply. The user reached out because they need your attention NOW. Acknowledge them right away, then resume your work.
|
|
19215
|
+
|
|
19216
|
+
\`${cmdPrefix()} sync send-dm --message "…"\` for short messages. For longer or markdown-rich messages, write to a file first and use \`--message-file <path>\` — this preserves formatting and avoids shell escaping issues. The conversation is in $ALOOK_CONVERSATION_ID, so you usually need no flags. You can send several times in one task.
|
|
19217
|
+
|
|
19218
|
+
Your messages are rendered as **markdown** in the user's app. Use formatting to make your responses clear and scannable — headers, bullet lists, code blocks, bold for key points. Don't send a wall of plain text when structure would help the reader. For anything beyond a one-liner, prefer \`--message-file\` so you can write proper markdown without fighting shell escaping.
|
|
19189
19219
|
|
|
19190
19220
|
### Attachments
|
|
19191
19221
|
When your task includes attachments, their local paths are listed in the prompt JSON under "attachments".
|
|
@@ -19652,7 +19682,7 @@ function releaseSteeringLock(baseDir, contextKey) {
|
|
|
19652
19682
|
}
|
|
19653
19683
|
|
|
19654
19684
|
// daemon/prompt.ts
|
|
19655
|
-
var DM_RESPONSE_NOTICE = "Reply with `alook sync send-dm` — that's the only thing the user sees;
|
|
19685
|
+
var DM_RESPONSE_NOTICE = "Reply with `alook sync send-dm` — that's the only thing the user sees; your task output and reasoning are not shown." + " Talk to them at milestones like a colleague would, and don't end your turn without sending what they need." + " If this task will take more than 30 seconds, send a quick ack first so the user knows you're on it.";
|
|
19656
19686
|
var EMAIL_NOTICE = "This task was triggered automatically by an incoming email. There is no human in this session." + " If you need to communicate with a human, you MUST send an email using the email sending tool." + " If you need more information or confirmation from the human, send them an email asking for it and then exit." + " Do not wait — when the human replies, a new task will be triggered automatically and you will be woken up with their response.";
|
|
19657
19687
|
var CALENDAR_NOTICE = "This task was triggered by a scheduled calendar event. There is no human in this session." + " If you need to communicate with a human, you MUST send an email using the email sending tool." + " If you need more information or confirmation, send an email asking for it and then exit." + " Do not wait — when the human replies, a new task will be triggered automatically and you will be woken up with their response.";
|
|
19658
19688
|
var ISSUE_NOTICE = "This task was triggered by an assigned issue. The issue_id is provided in this message." + " Use `alook issue show --issue_id <issue_id>` to read full context." + " Use `alook issue update --issue_id <issue_id> --status <status>` to change status." + " Use `alook issue comment --issue_id <issue_id> --body <text>` to leave a comment." + " CRITICAL — You MUST manage the issue status correctly. This is NOT optional:" + " 1. Set status to 'in_progress' when you start working." + " 2. If you complete the work yourself: leave a summary comment, then set status to 'review' as your last action. 'review' means there is actual completed work (code, artifact, result) ready for the owner to look at." + " 3. If you delegated work to colleagues and are waiting for their response: KEEP status as 'in_progress' and exit. This is expected — you will be woken up when they reply. Set 'review' only after all delegated work is confirmed complete." + " 4. NEVER set 'review' unless there is concrete completed work for the owner to review. Sending a plan to a colleague is NOT completed work." + " NEVER exit without doing at least one of: updating the status, or leaving a comment explaining what you did and what you're waiting for.";
|
|
@@ -19669,6 +19699,13 @@ function buildTaskObject(task, attachments) {
|
|
|
19669
19699
|
};
|
|
19670
19700
|
if (task.type === "user_dm_message") {
|
|
19671
19701
|
obj.notice = DM_RESPONSE_NOTICE;
|
|
19702
|
+
const ctx = task.context;
|
|
19703
|
+
if (ctx?.message_id) {
|
|
19704
|
+
obj.message_id = ctx.message_id;
|
|
19705
|
+
}
|
|
19706
|
+
if (ctx?.quoted_message) {
|
|
19707
|
+
obj.quoted_message = ctx.quoted_message;
|
|
19708
|
+
}
|
|
19672
19709
|
}
|
|
19673
19710
|
if (task.type === "email_notification") {
|
|
19674
19711
|
const ctx = task.context;
|
|
@@ -21015,11 +21052,14 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21015
21052
|
}
|
|
21016
21053
|
}
|
|
21017
21054
|
const cliConfig = loadCLIConfigForProfile(profile);
|
|
21018
|
-
const
|
|
21019
|
-
|
|
21020
|
-
|
|
21021
|
-
|
|
21022
|
-
|
|
21055
|
+
const allEntries = cliConfig.watched_workspaces || [];
|
|
21056
|
+
const workspaces = allEntries.filter((ws) => ws.status !== "registered" && !!ws.id);
|
|
21057
|
+
const registeredEntries = allEntries.filter((ws) => ws.status === "registered" && !ws.id);
|
|
21058
|
+
const standbyToken = registeredEntries[0]?.token ?? null;
|
|
21059
|
+
if (workspaces.length === 0 && standbyToken) {
|
|
21060
|
+
log10.info("No workspaces configured — daemon starting in standby mode with machine token. Awaiting workspace binding.");
|
|
21061
|
+
} else if (workspaces.length === 0) {
|
|
21062
|
+
log10.info("No workspaces configured — daemon starting in standby mode. Register a workspace to begin.");
|
|
21023
21063
|
}
|
|
21024
21064
|
const hasPerWorkspaceTokens = workspaces.every((ws) => !!ws.token);
|
|
21025
21065
|
if (!hasPerWorkspaceTokens) {
|
|
@@ -21050,6 +21090,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21050
21090
|
log10.info(`Detected providers: ${providers.map((p) => `${p.type}@${p.version}`).join(", ")}`);
|
|
21051
21091
|
const workspaceStates = [];
|
|
21052
21092
|
const runtimeIndex = new Map;
|
|
21093
|
+
let hadWorkspaces = workspaces.length > 0;
|
|
21053
21094
|
for (const ws of workspaces) {
|
|
21054
21095
|
const runtimes = providers.map((p) => ({
|
|
21055
21096
|
type: p.type,
|
|
@@ -21085,7 +21126,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21085
21126
|
});
|
|
21086
21127
|
}
|
|
21087
21128
|
}
|
|
21088
|
-
if (workspaceStates.length === 0) {
|
|
21129
|
+
if (workspaceStates.length === 0 && workspaces.length > 0) {
|
|
21089
21130
|
log10.error("No workspaces registered successfully.");
|
|
21090
21131
|
process.exit(1);
|
|
21091
21132
|
return;
|
|
@@ -21130,6 +21171,50 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21130
21171
|
} catch {}
|
|
21131
21172
|
log10.info(`Workspace ${workspaceId} deleted server-side — removed from config`);
|
|
21132
21173
|
}
|
|
21174
|
+
async function handleWorkspaceAdded(workspaceId, workspaceName, token) {
|
|
21175
|
+
if (workspaceStates.some((ws) => ws.workspaceId === workspaceId)) {
|
|
21176
|
+
log10.info(`Workspace ${workspaceId} already registered — ignoring workspace_added`);
|
|
21177
|
+
return;
|
|
21178
|
+
}
|
|
21179
|
+
log10.info(`Workspace ${workspaceId} bound — registering...`);
|
|
21180
|
+
const runtimes = providers.map((p) => ({ type: p.type, version: p.version }));
|
|
21181
|
+
try {
|
|
21182
|
+
const resp = await client.register(token, {
|
|
21183
|
+
workspace_id: workspaceId,
|
|
21184
|
+
daemon_id: config2.daemonId,
|
|
21185
|
+
device_name: config2.deviceName,
|
|
21186
|
+
cli_version: config2.cliVersion,
|
|
21187
|
+
workspaces_root: config2.workspacesRoot,
|
|
21188
|
+
runtimes
|
|
21189
|
+
});
|
|
21190
|
+
const runtimeIds = resp.runtimes.map((r) => r.id);
|
|
21191
|
+
workspaceStates.push({ workspaceId, token, runtimeIds });
|
|
21192
|
+
for (let i = 0;i < runtimeIds.length; i++) {
|
|
21193
|
+
runtimeIndex.set(runtimeIds[i], {
|
|
21194
|
+
id: runtimeIds[i],
|
|
21195
|
+
workspaceId,
|
|
21196
|
+
provider: providers[i].type
|
|
21197
|
+
});
|
|
21198
|
+
}
|
|
21199
|
+
hadWorkspaces = true;
|
|
21200
|
+
health.setRuntimeCount(workspaceStates.reduce((sum, w) => sum + w.runtimeIds.length, 0));
|
|
21201
|
+
try {
|
|
21202
|
+
const cfg = loadCLIConfigForProfile(profile);
|
|
21203
|
+
const watched = cfg.watched_workspaces || [];
|
|
21204
|
+
const registeredIdx = watched.findIndex((w) => w.token === token && w.status === "registered" && !w.id);
|
|
21205
|
+
if (registeredIdx !== -1) {
|
|
21206
|
+
watched[registeredIdx] = { id: workspaceId, name: workspaceName, token, status: "active", agent_ids: [] };
|
|
21207
|
+
} else if (!watched.some((w) => w.id === workspaceId)) {
|
|
21208
|
+
watched.push({ id: workspaceId, name: workspaceName, token, status: "active" });
|
|
21209
|
+
}
|
|
21210
|
+
cfg.watched_workspaces = watched;
|
|
21211
|
+
saveCLIConfigForProfile(profile, cfg);
|
|
21212
|
+
} catch {}
|
|
21213
|
+
log10.info(`Workspace ${workspaceId} added via WS push — ${runtimeIds.length} runtime(s)`);
|
|
21214
|
+
} catch (e) {
|
|
21215
|
+
log10.error(`Failed to register workspace ${workspaceId} from WS push`, e);
|
|
21216
|
+
}
|
|
21217
|
+
}
|
|
21133
21218
|
const pollCycle = async () => {
|
|
21134
21219
|
let remaining = config2.maxConcurrentTasks - activeTasks.size;
|
|
21135
21220
|
if (remaining <= 0)
|
|
@@ -21205,7 +21290,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21205
21290
|
for (const id of evictedIds) {
|
|
21206
21291
|
evictWorkspace(id);
|
|
21207
21292
|
}
|
|
21208
|
-
if (workspaceStates.length === 0) {
|
|
21293
|
+
if (workspaceStates.length === 0 && hadWorkspaces) {
|
|
21209
21294
|
log10.info("All workspaces evicted — shutting down");
|
|
21210
21295
|
shutdown();
|
|
21211
21296
|
}
|
|
@@ -21318,23 +21403,69 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21318
21403
|
}
|
|
21319
21404
|
break;
|
|
21320
21405
|
}
|
|
21406
|
+
case "daemon.workspace_added": {
|
|
21407
|
+
handleWorkspaceAdded(msg.workspaceId, msg.workspaceName, msg.token);
|
|
21408
|
+
break;
|
|
21409
|
+
}
|
|
21321
21410
|
}
|
|
21322
21411
|
}
|
|
21323
|
-
const
|
|
21412
|
+
const wsToken = firstToken || standbyToken;
|
|
21413
|
+
let wsClient = wsToken ? new DaemonWsClient({
|
|
21324
21414
|
serverURL: config2.serverURL,
|
|
21325
21415
|
daemonId: config2.daemonId,
|
|
21326
|
-
machineToken:
|
|
21416
|
+
machineToken: wsToken,
|
|
21327
21417
|
onMessage: handleWsPush,
|
|
21328
21418
|
onConnected: () => {
|
|
21329
|
-
|
|
21330
|
-
|
|
21419
|
+
if (workspaceStates.length > 0) {
|
|
21420
|
+
log10.info("WS connected — switching to low-frequency poll");
|
|
21421
|
+
updatePollInterval(config2.wsPollInterval);
|
|
21422
|
+
} else {
|
|
21423
|
+
log10.info("WS connected in standby mode — awaiting workspace binding");
|
|
21424
|
+
}
|
|
21331
21425
|
},
|
|
21332
21426
|
onDisconnected: () => {
|
|
21333
|
-
|
|
21334
|
-
|
|
21427
|
+
if (workspaceStates.length > 0) {
|
|
21428
|
+
log10.info("WS disconnected — reverting to high-frequency poll");
|
|
21429
|
+
updatePollInterval(config2.pollInterval);
|
|
21430
|
+
}
|
|
21335
21431
|
}
|
|
21336
21432
|
}) : null;
|
|
21337
21433
|
wsClient?.connect();
|
|
21434
|
+
const STANDBY_POLL_MS = 30000;
|
|
21435
|
+
let standbyPollTimer = null;
|
|
21436
|
+
if (standbyToken && workspaceStates.length === 0) {
|
|
21437
|
+
const standbyPollTick = async () => {
|
|
21438
|
+
if (workspaceStates.length > 0) {
|
|
21439
|
+
if (standbyPollTimer) {
|
|
21440
|
+
clearInterval(standbyPollTimer);
|
|
21441
|
+
standbyPollTimer = null;
|
|
21442
|
+
}
|
|
21443
|
+
return;
|
|
21444
|
+
}
|
|
21445
|
+
try {
|
|
21446
|
+
const runtimes = providers.map((p) => ({ type: p.type, version: p.version }));
|
|
21447
|
+
const resp = await client.checkStandby(standbyToken, {
|
|
21448
|
+
daemon_id: config2.daemonId,
|
|
21449
|
+
device_name: config2.deviceName,
|
|
21450
|
+
cli_version: config2.cliVersion,
|
|
21451
|
+
workspaces_root: config2.workspacesRoot,
|
|
21452
|
+
runtimes
|
|
21453
|
+
});
|
|
21454
|
+
if (!resp.standby && resp.runtimes.length > 0 && resp.workspaceId) {
|
|
21455
|
+
log10.info(`Standby poll: workspace ${resp.workspaceId} discovered via fallback`);
|
|
21456
|
+
await handleWorkspaceAdded(resp.workspaceId, "", standbyToken);
|
|
21457
|
+
if (standbyPollTimer) {
|
|
21458
|
+
clearInterval(standbyPollTimer);
|
|
21459
|
+
standbyPollTimer = null;
|
|
21460
|
+
}
|
|
21461
|
+
}
|
|
21462
|
+
} catch (e) {
|
|
21463
|
+
log10.debug("standby poll failed", { err: e instanceof Error ? e.message : String(e) });
|
|
21464
|
+
}
|
|
21465
|
+
};
|
|
21466
|
+
standbyPollTick();
|
|
21467
|
+
standbyPollTimer = setInterval(standbyPollTick, STANDBY_POLL_MS);
|
|
21468
|
+
}
|
|
21338
21469
|
const sweepTick = async () => {
|
|
21339
21470
|
for (const ws of workspaceStates) {
|
|
21340
21471
|
client.sweep(ws.token, config2.daemonId).catch((e) => {
|
|
@@ -21372,6 +21503,8 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21372
21503
|
clearInterval(pollTimer);
|
|
21373
21504
|
clearInterval(heartbeatTimer);
|
|
21374
21505
|
clearInterval(sweepTimer);
|
|
21506
|
+
if (standbyPollTimer)
|
|
21507
|
+
clearInterval(standbyPollTimer);
|
|
21375
21508
|
stopSkillScanner();
|
|
21376
21509
|
wsClient?.close();
|
|
21377
21510
|
const shutdownMs = restartRequested ? 30000 : Number(process.env.ALOOK_SHUTDOWN_TIMEOUT_MS) || 5000;
|
|
@@ -21420,7 +21553,7 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21420
21553
|
log10.info("SIGHUP received — reloading config...");
|
|
21421
21554
|
try {
|
|
21422
21555
|
const freshConfig = loadCLIConfigForProfile(profile);
|
|
21423
|
-
const freshWorkspaces = freshConfig.watched_workspaces || [];
|
|
21556
|
+
const freshWorkspaces = (freshConfig.watched_workspaces || []).filter((ws) => ws.status !== "registered" && !!ws.id);
|
|
21424
21557
|
const existingIds = new Set(workspaceStates.map((ws) => ws.workspaceId));
|
|
21425
21558
|
const newWorkspaces = freshWorkspaces.filter((ws) => ws.token && !existingIds.has(ws.id));
|
|
21426
21559
|
for (const ws of newWorkspaces) {
|
|
@@ -21450,7 +21583,27 @@ async function startDaemon(profile, serverUrl) {
|
|
|
21450
21583
|
}
|
|
21451
21584
|
}
|
|
21452
21585
|
if (newWorkspaces.length > 0) {
|
|
21586
|
+
hadWorkspaces = true;
|
|
21453
21587
|
health.setRuntimeCount(workspaceStates.reduce((sum, w) => sum + w.runtimeIds.length, 0));
|
|
21588
|
+
if (!wsClient && workspaceStates.length > 0) {
|
|
21589
|
+
const token = workspaceStates[0].token;
|
|
21590
|
+
wsClient = new DaemonWsClient({
|
|
21591
|
+
serverURL: config2.serverURL,
|
|
21592
|
+
daemonId: config2.daemonId,
|
|
21593
|
+
machineToken: token,
|
|
21594
|
+
onMessage: handleWsPush,
|
|
21595
|
+
onConnected: () => {
|
|
21596
|
+
log10.info("WS connected — switching to low-frequency poll");
|
|
21597
|
+
updatePollInterval(config2.wsPollInterval);
|
|
21598
|
+
},
|
|
21599
|
+
onDisconnected: () => {
|
|
21600
|
+
log10.info("WS disconnected — reverting to high-frequency poll");
|
|
21601
|
+
updatePollInterval(config2.pollInterval);
|
|
21602
|
+
}
|
|
21603
|
+
});
|
|
21604
|
+
wsClient.connect();
|
|
21605
|
+
log10.info("WS push client initialized after SIGHUP reload");
|
|
21606
|
+
}
|
|
21454
21607
|
log10.info(`Reload complete — now polling ${workspaceStates.length} workspace(s)`);
|
|
21455
21608
|
} else {
|
|
21456
21609
|
log10.info("Reload complete — no new workspaces found");
|
|
@@ -22967,7 +23120,7 @@ function versionCommand() {
|
|
|
22967
23120
|
|
|
22968
23121
|
// commands/update.ts
|
|
22969
23122
|
import { Command as Command11 } from "commander";
|
|
22970
|
-
function
|
|
23123
|
+
function updateCommand2() {
|
|
22971
23124
|
const cmd = new Command11("update").description("Update CLI to the latest version").action(async () => {
|
|
22972
23125
|
const current = getCurrentVersion();
|
|
22973
23126
|
console.log(`Current version: ${current}`);
|
|
@@ -23046,7 +23199,8 @@ function syncCommand() {
|
|
|
23046
23199
|
process.exit(1);
|
|
23047
23200
|
}
|
|
23048
23201
|
} else {
|
|
23049
|
-
content = opts.message ?? ""
|
|
23202
|
+
content = (opts.message ?? "").replace(/\\n/g, `
|
|
23203
|
+
`).replace(/\\t/g, "\t");
|
|
23050
23204
|
}
|
|
23051
23205
|
if (!content.trim()) {
|
|
23052
23206
|
console.error("Error: --message or --message-file is required (and must not be empty)");
|
|
@@ -23194,7 +23348,7 @@ program.addCommand(issueCommand());
|
|
|
23194
23348
|
program.addCommand(agentCommand());
|
|
23195
23349
|
program.addCommand(configCommand());
|
|
23196
23350
|
program.addCommand(versionCommand());
|
|
23197
|
-
program.addCommand(
|
|
23351
|
+
program.addCommand(updateCommand2());
|
|
23198
23352
|
program.addCommand(syncCommand());
|
|
23199
23353
|
program.addCommand(workspaceCommand());
|
|
23200
23354
|
program.parse();
|
package/dist/session-runner.js
CHANGED
|
@@ -14472,7 +14472,8 @@ var DaemonPushMessageSchema = exports_external.discriminatedUnion("type", [
|
|
|
14472
14472
|
exports_external.object({ type: exports_external.literal("daemon.evict"), workspaceId: exports_external.string() }),
|
|
14473
14473
|
exports_external.object({ type: exports_external.literal("daemon.update"), version: exports_external.string() }),
|
|
14474
14474
|
exports_external.object({ type: exports_external.literal("daemon.rescan") }),
|
|
14475
|
-
exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), agentId: exports_external.string().min(1), taskId: exports_external.string(), targetTaskId: exports_external.string() })
|
|
14475
|
+
exports_external.object({ type: exports_external.literal("daemon.kill"), workspaceId: exports_external.string(), agentId: exports_external.string().min(1), taskId: exports_external.string(), targetTaskId: exports_external.string() }),
|
|
14476
|
+
exports_external.object({ type: exports_external.literal("daemon.workspace_added"), workspaceId: exports_external.string(), workspaceName: exports_external.string(), token: exports_external.string() })
|
|
14476
14477
|
]);
|
|
14477
14478
|
var RegisterResponseSchema = exports_external.object({
|
|
14478
14479
|
runtimes: exports_external.array(exports_external.object({ id: exports_external.string() }))
|
|
@@ -14502,6 +14503,9 @@ var RegisterDaemonRequestSchema = exports_external.object({
|
|
|
14502
14503
|
workspaces_root: exports_external.string().optional().default(""),
|
|
14503
14504
|
runtimes: exports_external.array(DaemonRuntimeItemSchema).min(1)
|
|
14504
14505
|
});
|
|
14506
|
+
var BindWorkspaceRequestSchema = exports_external.object({
|
|
14507
|
+
workspace_id: exports_external.string().min(1)
|
|
14508
|
+
});
|
|
14505
14509
|
var DeregisterRequestSchema = exports_external.object({
|
|
14506
14510
|
daemon_id: exports_external.string().min(1)
|
|
14507
14511
|
});
|
|
@@ -14663,7 +14667,8 @@ var CreateConversationRequestSchema = exports_external.object({
|
|
|
14663
14667
|
channel: exports_external.string().optional()
|
|
14664
14668
|
});
|
|
14665
14669
|
var CreateMessageRequestSchema = exports_external.object({
|
|
14666
|
-
content: exports_external.string().min(1, "content is required")
|
|
14670
|
+
content: exports_external.string().min(1, "content is required"),
|
|
14671
|
+
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
14667
14672
|
});
|
|
14668
14673
|
var AgentDmRequestSchema = exports_external.object({
|
|
14669
14674
|
content: exports_external.string().min(1, "content is required"),
|
|
@@ -16646,6 +16651,8 @@ var machineToken = sqliteTable("machine_token", {
|
|
|
16646
16651
|
token: text("token").unique().notNull(),
|
|
16647
16652
|
name: text("name").notNull().default(""),
|
|
16648
16653
|
status: text("status").notNull().default("active"),
|
|
16654
|
+
hostname: text("hostname"),
|
|
16655
|
+
runtimesJson: text("runtimes_json"),
|
|
16649
16656
|
lastUsedAt: text("last_used_at"),
|
|
16650
16657
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
16651
16658
|
}, (t) => [index("idx_machine_token").on(t.token)]);
|
|
@@ -16780,7 +16787,24 @@ function isLocalUrl(url2) {
|
|
|
16780
16787
|
return false;
|
|
16781
16788
|
}
|
|
16782
16789
|
}
|
|
16790
|
+
function hasWindow() {
|
|
16791
|
+
return typeof globalThis !== "undefined" && "window" in globalThis;
|
|
16792
|
+
}
|
|
16793
|
+
function isTauri() {
|
|
16794
|
+
return hasWindow() && typeof window !== "undefined" && "__TAURI__" in window;
|
|
16795
|
+
}
|
|
16796
|
+
function isMobile() {
|
|
16797
|
+
if (!isTauri())
|
|
16798
|
+
return false;
|
|
16799
|
+
const ua = typeof navigator !== "undefined" ? navigator.userAgent : "";
|
|
16800
|
+
return /(android|iphone|ipad|ipod)/i.test(ua);
|
|
16801
|
+
}
|
|
16783
16802
|
function resolveMode(signals) {
|
|
16803
|
+
if (signals.tauri || isTauri()) {
|
|
16804
|
+
if (signals.tauriPlatform === "mobile" || isMobile())
|
|
16805
|
+
return "mobile";
|
|
16806
|
+
return "desktop";
|
|
16807
|
+
}
|
|
16784
16808
|
if (signals.nodeEnv === "development" && !signals.cmdPrefix)
|
|
16785
16809
|
return "dev";
|
|
16786
16810
|
if (signals.serverUrl && !signals.cmdPrefix && signals.nodeEnv !== "production" && isLocalUrl(signals.serverUrl))
|
|
@@ -16797,6 +16821,8 @@ function cliCommand(mode) {
|
|
|
16797
16821
|
return "pnpm dev:cli";
|
|
16798
16822
|
case "app":
|
|
16799
16823
|
return "npx @alook/app cli";
|
|
16824
|
+
case "desktop":
|
|
16825
|
+
case "mobile":
|
|
16800
16826
|
case "production":
|
|
16801
16827
|
return "npx @alook/cli";
|
|
16802
16828
|
}
|
|
@@ -16921,6 +16947,9 @@ class DaemonClient {
|
|
|
16921
16947
|
syncSkills(token, body) {
|
|
16922
16948
|
return this.request("POST", "/api/daemon/skills/sync", token, body);
|
|
16923
16949
|
}
|
|
16950
|
+
async checkStandby(token, body) {
|
|
16951
|
+
return this.request("POST", "/api/daemon/register", token, body);
|
|
16952
|
+
}
|
|
16924
16953
|
}
|
|
16925
16954
|
|
|
16926
16955
|
// daemon/agent/claude.ts
|
|
@@ -18310,6 +18339,7 @@ The CLI auto-detects your identity from the environment. No need to pass \`--age
|
|
|
18310
18339
|
### Command quick reference
|
|
18311
18340
|
| Capability | Command |
|
|
18312
18341
|
|---|---|
|
|
18342
|
+
| Send a message to the user | \`${cmdPrefix()} sync send-dm\` |
|
|
18313
18343
|
| Schedule / list / edit tasks | \`${cmdPrefix()} calendar set\` (also list, show, update, delete) |
|
|
18314
18344
|
| Upload a file for your owner | \`${cmdPrefix()} sync upload-artifact\` |
|
|
18315
18345
|
| Recruit a colleague agent | \`${cmdPrefix()} agent recruit\` |
|
|
@@ -18400,11 +18430,24 @@ Upload files for your owner to review in the app.
|
|
|
18400
18430
|
### Talking to the user
|
|
18401
18431
|
You're texting a colleague, not filing a report. The only thing the user sees is what you send with \`${cmdPrefix()} sync send-dm\` — your task output, reasoning, and tool calls are all off-screen. If you finish without sending, they got silence.
|
|
18402
18432
|
|
|
18433
|
+
\`${cmdPrefix()} sync send-dm\` sends a message to **the user** (your owner), not to a colleague agent. This is how you communicate with the human who gave you the task. Use email to talk to colleague agents.
|
|
18434
|
+
|
|
18403
18435
|
Message at milestones, the way a person would: acknowledge when you pick something up, share a real step forward or a fork in the road, and deliver the result. A quick task is often one message; a long one is a few well-spaced check-ins. Trust your read of the moment — don't narrate every small step, and don't go dark for a long stretch on something they're waiting on.
|
|
18404
18436
|
|
|
18405
18437
|
Say what a colleague would say, not a transcript — the answer in your own voice. (Email- and calendar-triggered tasks have no one watching the chat; use email there.)
|
|
18406
18438
|
|
|
18407
|
-
|
|
18439
|
+
**A real person is waiting on the other end.** Send updates at every milestone of your work — not just the final result. For any task longer than a minute:
|
|
18440
|
+
1. **Before you start**: tell them your plan ("I'll research X, then modify Y and Z")
|
|
18441
|
+
2. **During work**: update when you find something important, change direction, or hit a blocker ("Found the issue — it's in the auth module, fixing now")
|
|
18442
|
+
3. **When done**: deliver the clear result
|
|
18443
|
+
|
|
18444
|
+
Don't bundle everything into one giant message at the end. The user shouldn't have to sit in silence wondering what's happening. A one-line progress update costs nothing and keeps the human in the loop. But don't send repetitive or near-identical messages — each update should carry new information, not just restate what you already said.
|
|
18445
|
+
|
|
18446
|
+
**If the user sends you a message while you're working** — especially questions like "are you there?", "what's the status?", or unrelated requests — **respond to them immediately**. Don't finish your current task first and then reply. The user reached out because they need your attention NOW. Acknowledge them right away, then resume your work.
|
|
18447
|
+
|
|
18448
|
+
\`${cmdPrefix()} sync send-dm --message "…"\` for short messages. For longer or markdown-rich messages, write to a file first and use \`--message-file <path>\` — this preserves formatting and avoids shell escaping issues. The conversation is in $ALOOK_CONVERSATION_ID, so you usually need no flags. You can send several times in one task.
|
|
18449
|
+
|
|
18450
|
+
Your messages are rendered as **markdown** in the user's app. Use formatting to make your responses clear and scannable — headers, bullet lists, code blocks, bold for key points. Don't send a wall of plain text when structure would help the reader. For anything beyond a one-liner, prefer \`--message-file\` so you can write proper markdown without fighting shell escaping.
|
|
18408
18451
|
|
|
18409
18452
|
### Attachments
|
|
18410
18453
|
When your task includes attachments, their local paths are listed in the prompt JSON under "attachments".
|
|
@@ -18788,7 +18831,7 @@ function clearKillIntent(baseDir, taskId) {
|
|
|
18788
18831
|
}
|
|
18789
18832
|
|
|
18790
18833
|
// daemon/prompt.ts
|
|
18791
|
-
var DM_RESPONSE_NOTICE = "Reply with `alook sync send-dm` — that's the only thing the user sees;
|
|
18834
|
+
var DM_RESPONSE_NOTICE = "Reply with `alook sync send-dm` — that's the only thing the user sees; your task output and reasoning are not shown." + " Talk to them at milestones like a colleague would, and don't end your turn without sending what they need." + " If this task will take more than 30 seconds, send a quick ack first so the user knows you're on it.";
|
|
18792
18835
|
var EMAIL_NOTICE = "This task was triggered automatically by an incoming email. There is no human in this session." + " If you need to communicate with a human, you MUST send an email using the email sending tool." + " If you need more information or confirmation from the human, send them an email asking for it and then exit." + " Do not wait — when the human replies, a new task will be triggered automatically and you will be woken up with their response.";
|
|
18793
18836
|
var CALENDAR_NOTICE = "This task was triggered by a scheduled calendar event. There is no human in this session." + " If you need to communicate with a human, you MUST send an email using the email sending tool." + " If you need more information or confirmation, send an email asking for it and then exit." + " Do not wait — when the human replies, a new task will be triggered automatically and you will be woken up with their response.";
|
|
18794
18837
|
var ISSUE_NOTICE = "This task was triggered by an assigned issue. The issue_id is provided in this message." + " Use `alook issue show --issue_id <issue_id>` to read full context." + " Use `alook issue update --issue_id <issue_id> --status <status>` to change status." + " Use `alook issue comment --issue_id <issue_id> --body <text>` to leave a comment." + " CRITICAL — You MUST manage the issue status correctly. This is NOT optional:" + " 1. Set status to 'in_progress' when you start working." + " 2. If you complete the work yourself: leave a summary comment, then set status to 'review' as your last action. 'review' means there is actual completed work (code, artifact, result) ready for the owner to look at." + " 3. If you delegated work to colleagues and are waiting for their response: KEEP status as 'in_progress' and exit. This is expected — you will be woken up when they reply. Set 'review' only after all delegated work is confirmed complete." + " 4. NEVER set 'review' unless there is concrete completed work for the owner to review. Sending a plan to a colleague is NOT completed work." + " NEVER exit without doing at least one of: updating the status, or leaving a comment explaining what you did and what you're waiting for.";
|
|
@@ -18805,6 +18848,13 @@ function buildTaskObject(task, attachments) {
|
|
|
18805
18848
|
};
|
|
18806
18849
|
if (task.type === "user_dm_message") {
|
|
18807
18850
|
obj.notice = DM_RESPONSE_NOTICE;
|
|
18851
|
+
const ctx = task.context;
|
|
18852
|
+
if (ctx?.message_id) {
|
|
18853
|
+
obj.message_id = ctx.message_id;
|
|
18854
|
+
}
|
|
18855
|
+
if (ctx?.quoted_message) {
|
|
18856
|
+
obj.quoted_message = ctx.quoted_message;
|
|
18857
|
+
}
|
|
18808
18858
|
}
|
|
18809
18859
|
if (task.type === "email_notification") {
|
|
18810
18860
|
const ctx = task.context;
|