daily-focus 1.1.1-dev.3 → 1.2.0-dev.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/README.md +7 -5
- package/SETUP.md +63 -14
- package/dist/{audit-B5GKGH2P.js → audit-ITRXIX6T.js} +3 -3
- package/dist/{chunk-C6FXWHGT.js → chunk-26JG3D5C.js} +1 -1
- package/dist/{chunk-AUBJ2IDQ.js → chunk-AIQO2YAI.js} +2 -2
- package/dist/{chunk-NWYOCDUU.js → chunk-RNE27FQY.js} +2 -1
- package/dist/{chunk-GFCWPYEI.js → chunk-SX5FPS3A.js} +153 -6
- package/dist/{chunk-YWHADSKR.js → chunk-UY6HQLGE.js} +1 -1
- package/dist/cli.js +6 -6
- package/dist/{demo-AMK3Y4JZ.js → demo-KVFAVGO7.js} +3 -3
- package/dist/{init-YGOPPELJ.js → init-57WHZZNG.js} +1 -1
- package/dist/{seed-57RJGU4Y.js → seed-VALN2TCQ.js} +3 -3
- package/dist/{server-5HCA62YY.js → server-SOCS3SZJ.js} +43 -12
- package/dist/{service-63U4WBUH.js → service-NAAAV67B.js} +1 -1
- package/package.json +1 -1
- package/public/app.js +1 -1
package/README.md
CHANGED
|
@@ -131,11 +131,13 @@ notes live too; the reply streams in there, and you can follow up in the same
|
|
|
131
131
|
conversation. The panel stays on the item it was opened for while you move through
|
|
132
132
|
the list.
|
|
133
133
|
|
|
134
|
-
It
|
|
135
|
-
the thread, so it is waiting there when you open the mail; a reply to a
|
|
136
|
-
handed back as text for you to post. It never
|
|
137
|
-
has no checkout: fixing conflicts or a build is work for a real
|
|
138
|
-
this is for the questions you would otherwise answer by opening
|
|
134
|
+
It is told to read and draft, and nothing more. An email reply is created as a Gmail
|
|
135
|
+
draft on the thread, so it is waiting there when you open the mail; a reply to a
|
|
136
|
+
reviewer is handed back as text for you to post. It is told never to send, post, or
|
|
137
|
+
edit a file, and it has no checkout: fixing conflicts or a build is work for a real
|
|
138
|
+
coding session, and this is for the questions you would otherwise answer by opening
|
|
139
|
+
six tabs. How much of that the CLI itself enforces differs between Claude Code and
|
|
140
|
+
Codex; see [What the assistant can do](SETUP.md#what-the-assistant-can-do).
|
|
139
141
|
|
|
140
142
|
## The morning agent
|
|
141
143
|
|
package/SETUP.md
CHANGED
|
@@ -126,14 +126,23 @@ it finishes, and a chat where you can ask it why it did what it did. See
|
|
|
126
126
|
The agent reaches your sources the way the CLI does:
|
|
127
127
|
|
|
128
128
|
- **Codex** runs in its workspace-write sandbox with network access, confined to the
|
|
129
|
-
store. Connectors from the Codex app are found through its plugin catalogue
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
store. Connectors from the Codex app are found through its plugin catalogue, and
|
|
130
|
+
every write through one is refused: a connector tool counts as a write unless it
|
|
131
|
+
is marked read-only. A command that needs to leave the sandbox, such as `gh`
|
|
132
|
+
switching accounts, is decided by your own Codex approval settings, as it would be
|
|
133
|
+
in your Codex. With nobody set to approve, the agent reports that restriction; with
|
|
134
|
+
Codex's automatic reviewer, the reviewer decides. The dashboard won't start a run
|
|
135
|
+
on a Codex older than 0.144.0, which would ignore the refusal of connector writes;
|
|
136
|
+
update Codex if a run fails saying so.
|
|
132
137
|
- **Claude Code** may write `items.json` and nothing else in the store, and reach
|
|
133
138
|
its sources with only what `DAILY_FOCUS_AGENT_TOOLS` allows. The default covers
|
|
134
139
|
GitHub, Jira, web pages and the claude.ai Gmail connector. Add your calendar, chat
|
|
135
140
|
and document connectors to the list by name, or the brief will report them as
|
|
136
|
-
unreachable.
|
|
141
|
+
unreachable. The default allows every Gmail tool, sending included, and `gh` and
|
|
142
|
+
`acli` can write as well as read; the agent's instructions forbid changing
|
|
143
|
+
anything. To narrow Gmail to reading, name its tools instead of the connector, as
|
|
144
|
+
[What the assistant can do](#what-the-assistant-can-do) shows, without the two
|
|
145
|
+
draft tools.
|
|
137
146
|
|
|
138
147
|
Set `DAILY_FOCUS_AGENT_MODEL` and `DAILY_FOCUS_AGENT_EFFORT` if the CLI's own
|
|
139
148
|
defaults are not what you want for a brief; a brief is worth a high effort. If the
|
|
@@ -277,14 +286,53 @@ the CLI is configured with; `DAILY_FOCUS_ASSISTANT_MODEL` and
|
|
|
277
286
|
`DAILY_FOCUS_ASSISTANT_EFFORT` override them. If the server can't find the CLI,
|
|
278
287
|
set `DAILY_FOCUS_ASSISTANT_BIN` to its path.
|
|
279
288
|
|
|
280
|
-
The assistant
|
|
281
|
-
no checkout to work in. That is deliberate. Fixing merge conflicts or a
|
|
282
|
-
belongs in a real coding session; this panel is for the questions you'd
|
|
283
|
-
answer by opening six tabs. The Gmail draft is the one thing it creates,
|
|
284
|
-
Gmail tool the CLI has: with Claude Code that
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
the assistant hands you the text
|
|
289
|
+
The assistant is told to read and draft, and never to send, post or edit anything,
|
|
290
|
+
and it has no checkout to work in. That is deliberate. Fixing merge conflicts or a
|
|
291
|
+
failing build belongs in a real coding session; this panel is for the questions you'd
|
|
292
|
+
otherwise answer by opening six tabs. The Gmail draft is the one thing it creates,
|
|
293
|
+
and revises when you ask, through the Gmail tool the CLI has: with Claude Code that
|
|
294
|
+
is the claude.ai Gmail connector, which `DAILY_FOCUS_ASSISTANT_TOOLS` allows by
|
|
295
|
+
default; with Codex it is the Gmail app linked to your ChatGPT account, which the CLI
|
|
296
|
+
finds through its plugin catalogue. Without either, the assistant hands you the text
|
|
297
|
+
instead and says so.
|
|
298
|
+
|
|
299
|
+
### What the assistant can do
|
|
300
|
+
|
|
301
|
+
The assistant runs the CLI you already use, with the access that CLI already has.
|
|
302
|
+
The dashboard narrows some of that, and leaves the rest to the assistant's
|
|
303
|
+
instructions. What the CLI itself enforces:
|
|
304
|
+
|
|
305
|
+
- **Claude Code** may use only the tools in `DAILY_FOCUS_ASSISTANT_TOOLS`, and never
|
|
306
|
+
its file-editing tools, whatever the list says; anything else is refused rather
|
|
307
|
+
than asked about. Rules in your own Claude Code settings that allow a tool still
|
|
308
|
+
apply. The default list is broader than the assistant needs:
|
|
309
|
+
`mcp__claude_ai_Gmail` allows every tool on the Gmail connector, sending and
|
|
310
|
+
deleting included, and `gh` and `acli` can comment, merge and move tickets as well
|
|
311
|
+
as read. Only the instructions keep it from doing those. To hold it to reading
|
|
312
|
+
mail and drafting replies, name the Gmail tools instead of the connector:
|
|
313
|
+
|
|
314
|
+
```dotenv
|
|
315
|
+
DAILY_FOCUS_ASSISTANT_TOOLS=Bash(gh *),Bash(acli *),WebFetch,mcp__claude_ai_Gmail__search_threads,mcp__claude_ai_Gmail__get_thread,mcp__claude_ai_Gmail__get_message,mcp__claude_ai_Gmail__list_labels,mcp__claude_ai_Gmail__list_drafts,mcp__claude_ai_Gmail__get_draft,mcp__claude_ai_Gmail__create_draft,mcp__claude_ai_Gmail__update_draft
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
A tool left off the list is refused, so if the connector renames one, the
|
|
319
|
+
assistant reports that it can't reach your mail rather than working around it.
|
|
320
|
+
- **Codex** refuses every write through a connector (Gmail, Calendar, Drive, GitHub
|
|
321
|
+
and the other apps your ChatGPT account links) except creating and revising a
|
|
322
|
+
Gmail draft. A connector tool counts as a write unless it is marked read-only. A
|
|
323
|
+
tool you have told Codex to always allow, or an app given approval settings of its
|
|
324
|
+
own in your Codex config, keeps them. Computer use, the plugins that act in your
|
|
325
|
+
apps and browser with your sign-ins, is off unless you turn on
|
|
326
|
+
`DAILY_FOCUS_ASSISTANT_COMPUTER_USE`; the dashboard checks with Codex that it is
|
|
327
|
+
off before each session, and refuses to start one it can't confirm. Shell
|
|
328
|
+
commands follow your own Codex config: the sandbox keeps writes in the
|
|
329
|
+
assistant's working directory, and a command that needs to leave it is decided
|
|
330
|
+
by your Codex approval settings, as it would be in your own Codex. That keeps
|
|
331
|
+
`gh` working, and means it can write. All of this needs Codex 0.149.0 or
|
|
332
|
+
newer, and the page warns when yours is older. Before 0.144.0 Codex silently
|
|
333
|
+
ignores the refusal of connector writes, so the dashboard won't start the
|
|
334
|
+
assistant or the morning agent on it at all; before 0.149.0 it can't switch
|
|
335
|
+
computer use off, so the assistant won't answer while that setting is off.
|
|
288
336
|
|
|
289
337
|
Its instructions live in `prompts/assistant.md`, linked into the store as
|
|
290
338
|
`assistant.md` each time the dashboard starts. What it says stays in the chat in the panel: it
|
|
@@ -493,13 +541,14 @@ matters when `DAILY_FOCUS_FREE_WINDOWS` is off.
|
|
|
493
541
|
| `DAILY_FOCUS_ASSISTANT_BIN` | *the CLI's name* | Path to the assistant's CLI, for when the server's PATH lacks it. `~` is expanded |
|
|
494
542
|
| `DAILY_FOCUS_ASSISTANT_MODEL` | *the CLI's own* | Model passed to the CLI untouched: an alias like `opus`, or a full id. Unset passes no flag |
|
|
495
543
|
| `DAILY_FOCUS_ASSISTANT_EFFORT` | *the CLI's own* | Effort passed to the CLI untouched: `low`, `medium`, `high`, `xhigh` or `max`. Unset passes no flag |
|
|
496
|
-
| `DAILY_FOCUS_ASSISTANT_TOOLS` | `Bash(gh *),Bash(acli *),WebFetch,mcp__claude_ai_Gmail` | What Claude Code may use without asking, in its permission syntax, comma-separated. Editing tools are denied regardless. Ignored by Codex |
|
|
544
|
+
| `DAILY_FOCUS_ASSISTANT_TOOLS` | `Bash(gh *),Bash(acli *),WebFetch,mcp__claude_ai_Gmail` | What Claude Code may use without asking, in its permission syntax, comma-separated. Editing tools are denied regardless. The default allows every Gmail tool, sending included; see [What the assistant can do](#what-the-assistant-can-do) to narrow it. Ignored by Codex |
|
|
545
|
+
| `DAILY_FOCUS_ASSISTANT_COMPUTER_USE` | `off` | `on` lets the assistant use Codex's computer-use and browser plugins, which act in your apps and browser with your sign-ins. `off` switches them off for its runs. Ignored by Claude Code, which uses only the tools in its list |
|
|
497
546
|
| `DAILY_FOCUS_AGENT` | `off` | `codex` or `claude` lets the dashboard run the morning agent through that CLI: each scheduled morning, and from the refresh icon beside the brief's age. `off` hides it |
|
|
498
547
|
| `DAILY_FOCUS_AGENT_AT` | `07:00` | Local time, `HH:MM`, at which the dashboard starts the agent on each scheduled day, or as soon as it is running after that. `off` means it never starts one on its own; the refresh icon still does. Needs `DAILY_FOCUS_AGENT` |
|
|
499
548
|
| `DAILY_FOCUS_AGENT_BIN` | *the CLI's name* | Path to that CLI, for when the server's PATH lacks it. `~` is expanded |
|
|
500
549
|
| `DAILY_FOCUS_AGENT_MODEL` | *the CLI's own* | Model passed to the CLI untouched. Unset passes no flag |
|
|
501
550
|
| `DAILY_FOCUS_AGENT_EFFORT` | *the CLI's own* | Effort passed to the CLI untouched. Unset passes no flag |
|
|
502
|
-
| `DAILY_FOCUS_AGENT_TOOLS` | `Bash(gh *),Bash(acli *),WebFetch,mcp__claude_ai_Gmail` | What Claude Code may use to reach your sources, in its permission syntax, comma-separated. Writing `items.json`, and nothing else in the store, is allowed regardless. Ignored by Codex |
|
|
551
|
+
| `DAILY_FOCUS_AGENT_TOOLS` | `Bash(gh *),Bash(acli *),WebFetch,mcp__claude_ai_Gmail` | What Claude Code may use to reach your sources, in its permission syntax, comma-separated. Writing `items.json`, and nothing else in the store, is allowed regardless. The default allows every Gmail tool, sending included. Ignored by Codex |
|
|
503
552
|
|
|
504
553
|
## If something looks wrong
|
|
505
554
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readSystemZone,
|
|
3
3
|
startingZone
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-AIQO2YAI.js";
|
|
5
5
|
import {
|
|
6
6
|
linkState
|
|
7
7
|
} from "./chunk-QKT2FAB5.js";
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
readArchiveIndex,
|
|
13
13
|
resolveItems,
|
|
14
14
|
runContractChecks
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-26JG3D5C.js";
|
|
16
16
|
import {
|
|
17
17
|
BRIEF_REFRESH_GRACE_HOURS,
|
|
18
18
|
describeSchedule,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
resolveSchedule,
|
|
23
23
|
runsOn,
|
|
24
24
|
workingMsBetween
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-RNE27FQY.js";
|
|
26
26
|
import {
|
|
27
27
|
command
|
|
28
28
|
} from "./chunk-BC2W7NND.js";
|
|
@@ -353,7 +353,8 @@ function envAssistant(env) {
|
|
|
353
353
|
binPath: expandHome((env.DAILY_FOCUS_ASSISTANT_BIN ?? "").trim() || (agent ?? "claude")),
|
|
354
354
|
model: envString("DAILY_FOCUS_ASSISTANT_MODEL", "", env) || null,
|
|
355
355
|
effort: envString("DAILY_FOCUS_ASSISTANT_EFFORT", "", env) || null,
|
|
356
|
-
tools: tools.length > 0 ? tools : DEFAULT_ASSISTANT_TOOLS
|
|
356
|
+
tools: tools.length > 0 ? tools : DEFAULT_ASSISTANT_TOOLS,
|
|
357
|
+
computerUse: envFlag("DAILY_FOCUS_ASSISTANT_COMPUTER_USE", false, env)
|
|
357
358
|
};
|
|
358
359
|
}
|
|
359
360
|
var DEFAULT_ASSISTANT_TOOLS = ["Bash(gh *)", "Bash(acli *)", "WebFetch", "mcp__claude_ai_Gmail"];
|
|
@@ -1,3 +1,105 @@
|
|
|
1
|
+
// src/codex.ts
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
var GMAIL_CONNECTOR = "connector_2128aebfecb84f64a069897515042a44";
|
|
5
|
+
var CONNECTOR_POLICY = [
|
|
6
|
+
'apps._default.default_tools_approval_mode="writes"',
|
|
7
|
+
'apps._default.approvals_reviewer="user"'
|
|
8
|
+
];
|
|
9
|
+
var GMAIL_DRAFTS = ["create_draft", "update_draft"].map(
|
|
10
|
+
(tool) => `apps.${GMAIL_CONNECTOR}.tools.${tool}.approval_mode="approve"`
|
|
11
|
+
);
|
|
12
|
+
var COMPUTER_USE_PLUGINS = [
|
|
13
|
+
"unified-computer-use@openai-bundled",
|
|
14
|
+
"computer-use@openai-bundled",
|
|
15
|
+
"browser@openai-bundled",
|
|
16
|
+
"chrome@openai-bundled"
|
|
17
|
+
];
|
|
18
|
+
var COMPUTER_USE_SERVERS = ["node_repl", "cua_repl", "computer-use"];
|
|
19
|
+
var LIST_TIMEOUT_MS = 3e4;
|
|
20
|
+
var exec = promisify(execFile);
|
|
21
|
+
var defaultExec = async (file, args, { timeout }) => {
|
|
22
|
+
try {
|
|
23
|
+
const { stdout } = await exec(file, args, { timeout, maxBuffer: 16 * 1024 * 1024 });
|
|
24
|
+
return stdout;
|
|
25
|
+
} catch (err) {
|
|
26
|
+
const stderr = String(err.stderr ?? "").trim();
|
|
27
|
+
if (!stderr) throw err;
|
|
28
|
+
throw new Error(stderr.split("\n").slice(0, 2).join(" "));
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
function configArgs(overrides) {
|
|
32
|
+
return overrides.flatMap((override) => ["-c", override]);
|
|
33
|
+
}
|
|
34
|
+
async function enabledServers(bin, overrides, run) {
|
|
35
|
+
const output = await run(bin, ["mcp", "list", "--json", ...configArgs(overrides)], { timeout: LIST_TIMEOUT_MS });
|
|
36
|
+
let servers;
|
|
37
|
+
try {
|
|
38
|
+
servers = JSON.parse(output);
|
|
39
|
+
} catch {
|
|
40
|
+
throw new Error("codex mcp list --json printed something other than JSON");
|
|
41
|
+
}
|
|
42
|
+
if (!Array.isArray(servers)) throw new Error("codex mcp list --json printed something other than a list");
|
|
43
|
+
return servers.filter((server) => typeof server?.name === "string").filter((server) => server.enabled !== false).map((server) => server.name);
|
|
44
|
+
}
|
|
45
|
+
async function computerUseOff(bin, run = defaultExec) {
|
|
46
|
+
const overrides = COMPUTER_USE_PLUGINS.map((plugin) => `plugins.${plugin}.enabled=false`);
|
|
47
|
+
const left = (await enabledServers(bin, overrides, run)).filter((name) => COMPUTER_USE_SERVERS.includes(name));
|
|
48
|
+
if (left.length === 0) return overrides;
|
|
49
|
+
overrides.push(...left.map((name) => `mcp_servers.${name}.enabled=false`));
|
|
50
|
+
const still = (await enabledServers(bin, overrides, run)).filter((name) => COMPUTER_USE_SERVERS.includes(name));
|
|
51
|
+
if (still.length > 0) throw new Error(`Codex still starts ${still.join(", ")}`);
|
|
52
|
+
return overrides;
|
|
53
|
+
}
|
|
54
|
+
var CODEX_CONNECTOR_POLICY = "0.144.0";
|
|
55
|
+
var CODEX_MINIMUM = "0.149.0";
|
|
56
|
+
function versionParts(text) {
|
|
57
|
+
const match = /(\d+)\.(\d+)\.(\d+)/.exec(text);
|
|
58
|
+
return match ? match.slice(1, 4).map(Number) : null;
|
|
59
|
+
}
|
|
60
|
+
function olderThan(version, than) {
|
|
61
|
+
const a = versionParts(version);
|
|
62
|
+
const b = versionParts(than);
|
|
63
|
+
if (!a || !b) return false;
|
|
64
|
+
for (let i = 0; i < 3; i++) {
|
|
65
|
+
if (a[i] !== b[i]) return a[i] < b[i];
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
async function codexVersion(bin, run = defaultExec) {
|
|
70
|
+
try {
|
|
71
|
+
return versionParts(await run(bin, ["--version"], { timeout: LIST_TIMEOUT_MS }))?.join(".") ?? null;
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function refusalFor(version) {
|
|
77
|
+
if (!version || !olderThan(version, CODEX_CONNECTOR_POLICY)) return null;
|
|
78
|
+
return `Codex ${version} is older than ${CODEX_CONNECTOR_POLICY}, which ignores the dashboard's refusal of connector writes without saying so, so the dashboard won't run it. Update Codex`;
|
|
79
|
+
}
|
|
80
|
+
var CodexGate = class {
|
|
81
|
+
#bin;
|
|
82
|
+
#run;
|
|
83
|
+
#cleared = false;
|
|
84
|
+
constructor(bin, run = defaultExec) {
|
|
85
|
+
this.#bin = bin;
|
|
86
|
+
this.#run = run;
|
|
87
|
+
}
|
|
88
|
+
async refusal() {
|
|
89
|
+
if (this.#cleared) return null;
|
|
90
|
+
const version = await codexVersion(this.#bin, this.#run);
|
|
91
|
+
const refusal = refusalFor(version);
|
|
92
|
+
if (version && !refusal) this.#cleared = true;
|
|
93
|
+
return refusal;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
async function codexVersionWarning(bin, run = defaultExec) {
|
|
97
|
+
const version = await codexVersion(bin, run);
|
|
98
|
+
if (!version || !olderThan(version, CODEX_MINIMUM)) return null;
|
|
99
|
+
const consequence = olderThan(version, CODEX_CONNECTOR_POLICY) ? `Before ${CODEX_CONNECTOR_POLICY} Codex ignores the dashboard's refusal of connector writes without saying so, so the dashboard won't start the agent or the assistant on it.` : `Before ${CODEX_MINIMUM} Codex can't switch computer use off, so the assistant won't answer while computer use is off in its settings.`;
|
|
100
|
+
return `Codex ${version} is older than ${CODEX_MINIMUM}, the oldest the dashboard's limits on Codex work with. ${consequence} Update Codex.`;
|
|
101
|
+
}
|
|
102
|
+
|
|
1
103
|
// src/assistant.ts
|
|
2
104
|
import { spawn } from "node:child_process";
|
|
3
105
|
import { randomUUID } from "node:crypto";
|
|
@@ -166,13 +268,20 @@ var claudeAdapter = {
|
|
|
166
268
|
}
|
|
167
269
|
};
|
|
168
270
|
var codexAdapter = {
|
|
169
|
-
args({ prompt, sessionId, model, effort }) {
|
|
271
|
+
args({ prompt, sessionId, model, effort, drafts, codexConfig }) {
|
|
170
272
|
const args = ["exec"];
|
|
171
273
|
if (sessionId) args.push("resume");
|
|
172
274
|
args.push("--json", "--skip-git-repo-check");
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
275
|
+
const config = [
|
|
276
|
+
'sandbox_mode="workspace-write"',
|
|
277
|
+
"sandbox_workspace_write.network_access=true",
|
|
278
|
+
...CONNECTOR_POLICY,
|
|
279
|
+
...drafts ? GMAIL_DRAFTS : [],
|
|
280
|
+
...codexConfig
|
|
281
|
+
];
|
|
282
|
+
if (model) config.push(`model=${JSON.stringify(model)}`);
|
|
283
|
+
if (effort) config.push(`model_reasoning_effort=${JSON.stringify(effort)}`);
|
|
284
|
+
args.push(...configArgs(config));
|
|
176
285
|
if (sessionId) args.push(sessionId);
|
|
177
286
|
args.push(prompt);
|
|
178
287
|
return args;
|
|
@@ -219,10 +328,17 @@ var Assistant = class _Assistant {
|
|
|
219
328
|
config;
|
|
220
329
|
hooks;
|
|
221
330
|
spawnFn;
|
|
222
|
-
|
|
331
|
+
execFn;
|
|
332
|
+
/** Refuses a Codex too old to hold to the connector rules; see `codex.ts`. */
|
|
333
|
+
codexGate;
|
|
334
|
+
/** What switches computer use off, once Codex has said; see `computerUseConfig`. */
|
|
335
|
+
codexOff = null;
|
|
336
|
+
constructor(config, hooks, spawnFn = spawn, execFn) {
|
|
223
337
|
this.config = config;
|
|
224
338
|
this.hooks = hooks;
|
|
225
339
|
this.spawnFn = spawnFn;
|
|
340
|
+
this.execFn = execFn;
|
|
341
|
+
this.codexGate = new CodexGate(config.assistant.binPath, execFn);
|
|
226
342
|
}
|
|
227
343
|
get enabled() {
|
|
228
344
|
return this.config.assistant.agent !== null;
|
|
@@ -329,6 +445,19 @@ ${typed}` : action.request : typed;
|
|
|
329
445
|
});
|
|
330
446
|
const prompt = previous ? resolved.request : composePrompt(await this.instructions(), context, resolved.request, this.config.profile);
|
|
331
447
|
await mkdir(this.config.assistantDir, { recursive: true });
|
|
448
|
+
const refusal = agent === "codex" ? await this.codexGate.refusal() : null;
|
|
449
|
+
if (refusal) {
|
|
450
|
+
await this.finish(turn, { ok: false, error: refusal });
|
|
451
|
+
return turn;
|
|
452
|
+
}
|
|
453
|
+
let codexConfig;
|
|
454
|
+
try {
|
|
455
|
+
codexConfig = await this.computerUseConfig();
|
|
456
|
+
} catch (err) {
|
|
457
|
+
const error = err.code === "ENOENT" ? this.spawnError(err) : `computer use is off in the settings, and Codex could not confirm it: ${err.message}. Update Codex, or turn computer use on for the assistant`;
|
|
458
|
+
await this.finish(turn, { ok: false, error });
|
|
459
|
+
return turn;
|
|
460
|
+
}
|
|
332
461
|
const adapter = ADAPTERS[agent];
|
|
333
462
|
const args = adapter.args({
|
|
334
463
|
prompt,
|
|
@@ -336,7 +465,9 @@ ${typed}` : action.request : typed;
|
|
|
336
465
|
model: this.config.assistant.model,
|
|
337
466
|
effort: this.config.assistant.effort,
|
|
338
467
|
tools: this.config.assistant.tools,
|
|
339
|
-
denyEdits: true
|
|
468
|
+
denyEdits: true,
|
|
469
|
+
drafts: true,
|
|
470
|
+
codexConfig
|
|
340
471
|
});
|
|
341
472
|
let child;
|
|
342
473
|
try {
|
|
@@ -421,6 +552,20 @@ ${event.text}` : event.text;
|
|
|
421
552
|
}
|
|
422
553
|
this.hooks.onChange();
|
|
423
554
|
}
|
|
555
|
+
/**
|
|
556
|
+
* What switches computer use off, when the assistant runs Codex and the user
|
|
557
|
+
* hasn't turned it on. Asked of Codex once and kept for the life of the
|
|
558
|
+
* process; settings that change it restart the dashboard anyway. A failure
|
|
559
|
+
* isn't kept, so the next turn asks again.
|
|
560
|
+
*/
|
|
561
|
+
computerUseConfig() {
|
|
562
|
+
if (this.config.assistant.agent !== "codex" || this.config.assistant.computerUse) return Promise.resolve([]);
|
|
563
|
+
this.codexOff ??= computerUseOff(this.config.assistant.binPath, this.execFn).catch((err) => {
|
|
564
|
+
this.codexOff = null;
|
|
565
|
+
throw err;
|
|
566
|
+
});
|
|
567
|
+
return this.codexOff;
|
|
568
|
+
}
|
|
424
569
|
spawnError(err) {
|
|
425
570
|
const code = err.code;
|
|
426
571
|
const bin = this.config.assistant.binPath;
|
|
@@ -450,6 +595,8 @@ ${event.text}` : event.text;
|
|
|
450
595
|
};
|
|
451
596
|
|
|
452
597
|
export {
|
|
598
|
+
CodexGate,
|
|
599
|
+
codexVersionWarning,
|
|
453
600
|
QUICK_ACTIONS,
|
|
454
601
|
ADAPTERS,
|
|
455
602
|
Assistant
|
package/dist/cli.js
CHANGED
|
@@ -49,7 +49,7 @@ function shouldOpen(flag) {
|
|
|
49
49
|
return flag && process.stdout.isTTY === true && !process.env.CI;
|
|
50
50
|
}
|
|
51
51
|
async function demo(open, profile) {
|
|
52
|
-
const { demoEnv, demoIntegrations, demoProfile, writeDemoStore } = await import("./demo-
|
|
52
|
+
const { demoEnv, demoIntegrations, demoProfile, writeDemoStore } = await import("./demo-KVFAVGO7.js");
|
|
53
53
|
const chosen = demoProfile(profile);
|
|
54
54
|
const store = await mkdtemp(join(tmpdir(), "daily-focus-demo-"));
|
|
55
55
|
process.on("exit", () => rmSync(store, { recursive: true, force: true }));
|
|
@@ -65,7 +65,7 @@ async function demo(open, profile) {
|
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
67
|
async function start(open, onStarted, options = {}) {
|
|
68
|
-
const { runServer } = await import("./server-
|
|
68
|
+
const { runServer } = await import("./server-SOCS3SZJ.js");
|
|
69
69
|
await runServer({
|
|
70
70
|
...options,
|
|
71
71
|
onStarted: (url) => {
|
|
@@ -162,20 +162,20 @@ async function main(argv) {
|
|
|
162
162
|
}
|
|
163
163
|
return start(open);
|
|
164
164
|
case "service": {
|
|
165
|
-
const { main: service } = await import("./service-
|
|
165
|
+
const { main: service } = await import("./service-NAAAV67B.js");
|
|
166
166
|
process.exitCode = await service(values.remove ? ["--remove"] : []);
|
|
167
167
|
return;
|
|
168
168
|
}
|
|
169
169
|
// These three are scripts that do their work as they load, and read their
|
|
170
170
|
// own flags from the command line.
|
|
171
171
|
case "init":
|
|
172
|
-
await import("./init-
|
|
172
|
+
await import("./init-57WHZZNG.js");
|
|
173
173
|
return;
|
|
174
174
|
case "audit":
|
|
175
|
-
await import("./audit-
|
|
175
|
+
await import("./audit-ITRXIX6T.js");
|
|
176
176
|
return;
|
|
177
177
|
case "seed":
|
|
178
|
-
await import("./seed-
|
|
178
|
+
await import("./seed-VALN2TCQ.js");
|
|
179
179
|
return;
|
|
180
180
|
case "build-calendar":
|
|
181
181
|
return buildCalendar();
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
QUICK_ACTIONS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SX5FPS3A.js";
|
|
4
4
|
import {
|
|
5
5
|
dateFromNow,
|
|
6
6
|
sampleBrief,
|
|
7
7
|
sampleGeneratedAt
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-UY6HQLGE.js";
|
|
9
9
|
import {
|
|
10
10
|
PROFILES,
|
|
11
11
|
localDateKey
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-RNE27FQY.js";
|
|
13
13
|
import "./chunk-BC2W7NND.js";
|
|
14
14
|
|
|
15
15
|
// src/demo.ts
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
sampleBrief
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-UY6HQLGE.js";
|
|
4
4
|
import {
|
|
5
5
|
Store
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-26JG3D5C.js";
|
|
7
7
|
import {
|
|
8
8
|
loadConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-RNE27FQY.js";
|
|
10
10
|
import {
|
|
11
11
|
command
|
|
12
12
|
} from "./chunk-BC2W7NND.js";
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ADAPTERS,
|
|
3
|
-
Assistant
|
|
4
|
-
|
|
3
|
+
Assistant,
|
|
4
|
+
CodexGate,
|
|
5
|
+
codexVersionWarning
|
|
6
|
+
} from "./chunk-SX5FPS3A.js";
|
|
5
7
|
import {
|
|
6
8
|
FOCUS_TEMPLATE,
|
|
7
9
|
sourcesTemplate
|
|
@@ -9,7 +11,7 @@ import {
|
|
|
9
11
|
import {
|
|
10
12
|
TimeZoneKeeper,
|
|
11
13
|
validZone
|
|
12
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-AIQO2YAI.js";
|
|
13
15
|
import {
|
|
14
16
|
describeLink,
|
|
15
17
|
linkStore,
|
|
@@ -24,7 +26,7 @@ import {
|
|
|
24
26
|
stopSession,
|
|
25
27
|
tempPathFor,
|
|
26
28
|
writeJsonAtomic
|
|
27
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-26JG3D5C.js";
|
|
28
30
|
import {
|
|
29
31
|
CLI_NAMES,
|
|
30
32
|
LAUNCH_SETTINGS,
|
|
@@ -40,7 +42,7 @@ import {
|
|
|
40
42
|
readDotEnv,
|
|
41
43
|
resolveSchedule,
|
|
42
44
|
scheduledRunDue
|
|
43
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-RNE27FQY.js";
|
|
44
46
|
import {
|
|
45
47
|
PACKAGED,
|
|
46
48
|
command,
|
|
@@ -2506,10 +2508,13 @@ var AgentRunner = class {
|
|
|
2506
2508
|
config;
|
|
2507
2509
|
hooks;
|
|
2508
2510
|
spawnFn;
|
|
2509
|
-
|
|
2511
|
+
/** Refuses a Codex too old to hold to the connector rules; see `codex.ts`. */
|
|
2512
|
+
codexGate;
|
|
2513
|
+
constructor(config, hooks, spawnFn = spawn, execFn) {
|
|
2510
2514
|
this.config = config;
|
|
2511
2515
|
this.hooks = hooks;
|
|
2512
2516
|
this.spawnFn = spawnFn;
|
|
2517
|
+
this.codexGate = new CodexGate(config.agent.binPath, execFn);
|
|
2513
2518
|
}
|
|
2514
2519
|
get enabled() {
|
|
2515
2520
|
return this.config.agent.cli !== null;
|
|
@@ -2632,7 +2637,9 @@ var AgentRunner = class {
|
|
|
2632
2637
|
model: this.config.agent.model,
|
|
2633
2638
|
effort: this.config.agent.effort,
|
|
2634
2639
|
tools: [...briefWritingTools(), ...this.config.agent.tools],
|
|
2635
|
-
denyEdits: false
|
|
2640
|
+
denyEdits: false,
|
|
2641
|
+
drafts: false,
|
|
2642
|
+
codexConfig: []
|
|
2636
2643
|
});
|
|
2637
2644
|
await this.spawnInto(run6, null, args, RUN_TIMEOUT_MS, "gave up after an hour");
|
|
2638
2645
|
return run6;
|
|
@@ -2678,7 +2685,9 @@ var AgentRunner = class {
|
|
|
2678
2685
|
effort: this.config.agent.effort,
|
|
2679
2686
|
// The sources, and not the brief: a chat is given nothing to write.
|
|
2680
2687
|
tools: this.config.agent.tools,
|
|
2681
|
-
denyEdits: true
|
|
2688
|
+
denyEdits: true,
|
|
2689
|
+
drafts: false,
|
|
2690
|
+
codexConfig: []
|
|
2682
2691
|
});
|
|
2683
2692
|
await this.spawnInto(run6, turn, args, TURN_TIMEOUT_MS, "gave up after ten minutes");
|
|
2684
2693
|
return turn;
|
|
@@ -2692,6 +2701,11 @@ var AgentRunner = class {
|
|
|
2692
2701
|
const cli = run6.cli;
|
|
2693
2702
|
const adapter = ADAPTERS[cli];
|
|
2694
2703
|
await mkdir(this.config.dataDir, { recursive: true });
|
|
2704
|
+
const refusal2 = cli === "codex" ? await this.codexGate.refusal() : null;
|
|
2705
|
+
if (refusal2) {
|
|
2706
|
+
await this.finish(run6, turn, { ok: false, error: refusal2 });
|
|
2707
|
+
return;
|
|
2708
|
+
}
|
|
2695
2709
|
let child;
|
|
2696
2710
|
try {
|
|
2697
2711
|
child = this.spawnFn(this.config.agent.binPath, args, {
|
|
@@ -3015,7 +3029,7 @@ var SETTINGS = [
|
|
|
3015
3029
|
key: "DAILY_FOCUS_AGENT_TOOLS",
|
|
3016
3030
|
group: "agent",
|
|
3017
3031
|
label: "Tools Claude Code may use",
|
|
3018
|
-
help: "In Claude Code's permission syntax, comma-separated. Add your calendar, chat and document connectors by name, or the brief reports them as unreachable. Ignored by Codex.",
|
|
3032
|
+
help: "In Claude Code's permission syntax, comma-separated. Add your calendar, chat and document connectors by name, or the brief reports them as unreachable. The default allows every Gmail tool, sending included; name the tools you want instead to narrow it. Ignored by Codex.",
|
|
3019
3033
|
kind: "list",
|
|
3020
3034
|
fallback: always("Bash(gh *), Bash(acli *), WebFetch, mcp__claude_ai_Gmail"),
|
|
3021
3035
|
advanced: true
|
|
@@ -3042,7 +3056,7 @@ var SETTINGS = [
|
|
|
3042
3056
|
key: "DAILY_FOCUS_ASSISTANT",
|
|
3043
3057
|
group: "assistant",
|
|
3044
3058
|
label: "Answer questions with",
|
|
3045
|
-
help: "Puts an assistant in every item's panel
|
|
3059
|
+
help: "Puts an assistant in every item's panel, told to read and draft and never to send, post or edit. Under Codex, every connector write but a Gmail draft is refused.",
|
|
3046
3060
|
kind: "choice",
|
|
3047
3061
|
choices: OFF_OR_CLI,
|
|
3048
3062
|
fallback: always("Off")
|
|
@@ -3068,11 +3082,19 @@ var SETTINGS = [
|
|
|
3068
3082
|
key: "DAILY_FOCUS_ASSISTANT_TOOLS",
|
|
3069
3083
|
group: "assistant",
|
|
3070
3084
|
label: "Tools Claude Code may use",
|
|
3071
|
-
help: "In Claude Code's permission syntax, comma-separated. Editing tools are denied whatever this says. Ignored by Codex.",
|
|
3085
|
+
help: "In Claude Code's permission syntax, comma-separated. Editing tools are denied whatever this says. The default allows every Gmail tool, sending included; name the tools you want instead to narrow it. Ignored by Codex.",
|
|
3072
3086
|
kind: "list",
|
|
3073
3087
|
fallback: always("Bash(gh *), Bash(acli *), WebFetch, mcp__claude_ai_Gmail"),
|
|
3074
3088
|
advanced: true
|
|
3075
3089
|
},
|
|
3090
|
+
{
|
|
3091
|
+
key: "DAILY_FOCUS_ASSISTANT_COMPUTER_USE",
|
|
3092
|
+
group: "assistant",
|
|
3093
|
+
label: "Computer use",
|
|
3094
|
+
help: "Codex only. On lets the assistant use Codex's computer-use and browser plugins, which act in your apps and browser with your sign-ins. Off switches them off for its runs. Claude Code uses only the tools in its list.",
|
|
3095
|
+
kind: "flag",
|
|
3096
|
+
fallback: always("Off")
|
|
3097
|
+
},
|
|
3076
3098
|
{
|
|
3077
3099
|
key: "DAILY_FOCUS_ASSISTANT_BIN",
|
|
3078
3100
|
group: "assistant",
|
|
@@ -3722,6 +3744,15 @@ async function startServer(env, options = {}) {
|
|
|
3722
3744
|
restartWhenIdle();
|
|
3723
3745
|
}
|
|
3724
3746
|
});
|
|
3747
|
+
let codexWarnings = [];
|
|
3748
|
+
const codexBins = /* @__PURE__ */ new Set();
|
|
3749
|
+
if (config.agent.cli === "codex") codexBins.add(config.agent.binPath);
|
|
3750
|
+
if (config.assistant.agent === "codex") codexBins.add(config.assistant.binPath);
|
|
3751
|
+
void Promise.all([...codexBins].map((bin) => codexVersionWarning(bin))).then((found) => {
|
|
3752
|
+
codexWarnings = [...new Set(found.filter((warning) => warning !== null))];
|
|
3753
|
+
for (const warning of codexWarnings) console.warn(`[daily-focus] ${warning}`);
|
|
3754
|
+
if (codexWarnings.length > 0) void broadcast();
|
|
3755
|
+
});
|
|
3725
3756
|
function requestRestart() {
|
|
3726
3757
|
restart = options.onRestart ? "waiting" : "manual";
|
|
3727
3758
|
restartWhenIdle();
|
|
@@ -3749,7 +3780,7 @@ async function startServer(env, options = {}) {
|
|
|
3749
3780
|
tickets: tickets.view(actions, now),
|
|
3750
3781
|
assistant: assistant.view(),
|
|
3751
3782
|
agentRun: agent.view(now),
|
|
3752
|
-
setupWarnings: [...configWarnings, ...linkWarning ? [linkWarning] : []],
|
|
3783
|
+
setupWarnings: [...configWarnings, ...codexWarnings, ...linkWarning ? [linkWarning] : []],
|
|
3753
3784
|
restart,
|
|
3754
3785
|
setup,
|
|
3755
3786
|
assetVersion,
|
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -3777,7 +3777,7 @@ function HelpDialog(_props) {
|
|
|
3777
3777
|
el(
|
|
3778
3778
|
"p",
|
|
3779
3779
|
{ class: "help__note" },
|
|
3780
|
-
`Done, snoozed and dismissed all tell the morning agent to leave an item out of tomorrow's brief. A note is sent along with it as free text. On both boards only snooze and note apply: a pull request stays until it's merged or closed, and a ticket whose status looks wrong is put right in Jira rather than here. Click a card to open its panel, where its notes and the assistant live; the panel stays on the item it was opened for. The assistant is a CLI run on your machine
|
|
3780
|
+
`Done, snoozed and dismissed all tell the morning agent to leave an item out of tomorrow's brief. A note is sent along with it as free text. On both boards only snooze and note apply: a pull request stays until it's merged or closed, and a ticket whose status looks wrong is put right in Jira rather than here. Click a card to open its panel, where its notes and the assistant live; the panel stays on the item it was opened for. The assistant is a CLI run on your machine, told to read and draft, never to send or post. Click "updated \u2026 ago" in the header to open the morning agent's latest run: its report, earlier runs, and a place to ask it why it did what it did.`
|
|
3781
3781
|
),
|
|
3782
3782
|
el("form", { method: "dialog" }, el("button", { type: "submit", class: "button" }, "Close"))
|
|
3783
3783
|
);
|