taskchef 7.19.0 → 7.19.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "7.19.0",
3
+ "version": "7.19.1",
4
4
  "description": "Dispatch work from a data-only workspace to visible Codex project tasks.",
5
5
  "author": {
6
6
  "name": "Favo Yang",
package/README.md CHANGED
@@ -271,6 +271,11 @@ and its activity timeline remain unchanged. If the bundled CLI is unavailable,
271
271
  the dashboard does not fall back to another `codex` executable from `PATH`.
272
272
  The header shows the running TaskChef package version reported by the same
273
273
  bounded health identity used for compatible-listener checks.
274
+ The canonical port is owned by a dashboard initialized in the TaskChef MCP host
275
+ before its tool transport connects. Health identity records an `mcp` launcher,
276
+ and MCP recovery reuses only another exact-compatible MCP-launched dashboard;
277
+ a foreground `taskchef dashboard` process is intentionally standalone so its
278
+ child commands cannot silently inherit an agent-shell sandbox.
274
279
  Task and result times are relative through 29 days (with minute detail for the
275
280
  first six hours), then use a locale-aware calendar date. Each time is a keyboard-
276
281
  accessible toggle for its full locale-aware date and time, and one shared
@@ -281,16 +286,17 @@ revision, so reconnects and non-semantic rewrites do not replay a notice and a
281
286
  later task state cannot rewrite an older notice. A notice remains readable if
282
287
  its task disappears; selecting it then explains that current details are no
283
288
  longer available.
284
- It does not mutate TaskChef data and prints its local URL.
285
- When a compatible foreground dashboard already owns port 3210,
286
- `ensure_dashboard` reuses it but does not take ownership. If an unknown,
287
- different-workspace, or stale-version process owns the port, TaskChef reports a
288
- concise conflict and never kills or replaces that process. The foreground CLI
289
- similarly asks you to stop the listener or choose another `--port`.
289
+ It does not mutate TaskChef data and prints its local URL. A foreground
290
+ dashboard identifies itself as standalone and is never reused on the canonical
291
+ MCP port. If a standalone, unknown, different-workspace, or stale-version
292
+ process owns port 3210, TaskChef reports a concise conflict and never kills or
293
+ replaces that process. The foreground CLI similarly asks you to stop the
294
+ listener or choose another `--port`.
290
295
 
291
296
  The health endpoint contains only a fixed service marker, health schema,
292
- TaskChef version, dashboard-server version, and canonical workspace. It exposes
293
- no task data, credentials, environment variables, process control, or secrets.
297
+ TaskChef version, dashboard-server version, canonical workspace, and launcher.
298
+ It exposes no task data, credentials, environment variables, process control,
299
+ or secrets.
294
300
 
295
301
  ![TaskChef dashboard identity and version](docs/images/dashboard-identity.jpg)
296
302
 
@@ -351,13 +357,15 @@ does not necessarily reload Codex. Then run `$taskchef-dashboard` (or call
351
357
 
352
358
  - the expected released TaskChef version;
353
359
  - the expected dashboard protocol `serverVersion`;
360
+ - the `mcp` dashboard launcher;
354
361
  - the canonical TaskChef workspace path;
355
362
  - the canonical `http://127.0.0.1:3210/` URL.
356
363
 
357
364
  The release-install sequence is therefore: install plugin, activate or reload
358
365
  the new MCP process, ensure the dashboard, then verify TaskChef version,
359
- protocol `serverVersion`, canonical workspace, and URL. Exact-compatible
360
- servers may be reused; unknown listeners remain untouched.
366
+ protocol `serverVersion`, `mcp` launcher, canonical workspace, and URL.
367
+ Exact-compatible MCP servers may be reused; standalone and unknown listeners
368
+ remain untouched.
361
369
 
362
370
  ## Development
363
371
 
package/docs/spec.md CHANGED
@@ -121,13 +121,16 @@ fields MUST NOT change after recording.
121
121
 
122
122
  ## Required lifecycle
123
123
 
124
- When the canonical TaskChef MCP server finishes connecting, it MUST invoke the
125
- same serialized dashboard ensure path once by default. It MUST read the
124
+ Before the canonical TaskChef MCP server connects and exposes its transport, it
125
+ MUST invoke the same serialized dashboard ensure path once by default. It MUST read the
126
126
  canonical configuration and skip this only for explicit
127
127
  `dashboard.autostart: false`. Initialization failures, invalid workspace state,
128
128
  port conflicts, and dashboard errors MUST NOT prevent tool registration or MCP
129
129
  availability. They MUST emit only a bounded non-sensitive diagnostic through
130
130
  the MCP process logging channel. MCP initialization MUST NOT open a browser.
131
+ If transport connection fails after dashboard startup, the MCP server MUST
132
+ best-effort close both its owned dashboard and partially attached transport,
133
+ then propagate the original connection failure even when cleanup also fails.
131
134
 
132
135
  At the start of every dispatcher turn, the dispatcher SHOULD call
133
136
  `ensure_dashboard` best-effort. Failure MUST NOT block direct TaskChef answers,
@@ -228,6 +231,7 @@ HTTP server; it does not mutate dispatcher workspace files.
228
231
  ```text
229
232
  { dashboard: {
230
233
  action: "started" | "reused",
234
+ launcher: "mcp",
231
235
  url: "http://127.0.0.1:3210/",
232
236
  workspace: string,
233
237
  taskchefVersion: string,
@@ -235,20 +239,22 @@ HTTP server; it does not mutate dispatcher workspace files.
235
239
  } }
236
240
  ```
237
241
 
238
- Calls MUST serialize within one MCP process. The first call starts an owned
239
- dashboard or reuses an exact compatible listener; later and concurrent calls
240
- are idempotent and report reuse after the single start. The stable default MUST
242
+ MCP initialization MUST complete its best-effort dashboard start before exposing
243
+ the MCP transport. Calls MUST serialize within one MCP process. The first call
244
+ starts an owned dashboard or reuses an exact compatible MCP-launched listener;
245
+ later and concurrent calls are idempotent and report reuse after the single start. The stable default MUST
241
246
  bind only to `127.0.0.1:3210` and MUST NOT accept a model-supplied workspace,
242
247
  host, or port.
243
248
 
244
249
  Before reuse, TaskChef MUST query a bounded loopback identity endpoint and
245
250
  require the exact fixed service/schema, TaskChef version, dashboard-server
246
- version, and canonical workspace. An unknown, malformed, different-workspace,
247
- or stale-version listener MUST produce a concise actionable conflict. TaskChef
251
+ version, canonical workspace, and `mcp` launcher identity. A standalone,
252
+ unknown, malformed, different-workspace, or stale-version listener MUST produce a concise actionable conflict. TaskChef
248
253
  MUST NOT kill, replace, signal, or otherwise control that listener. A startup
249
254
  failure MUST leave no owned listener. The MCP server MUST close its owned
250
- dashboard when its transport or process shuts down; it MUST NOT close a reused
251
- external foreground server.
255
+ dashboard when its transport or process shuts down. This keeps dashboard child
256
+ operations in the MCP host environment and prevents an agent-shell or foreground
257
+ CLI dashboard from being mistaken for the canonical MCP-owned runtime.
252
258
 
253
259
  The packaged `$taskchef-dashboard` skill MUST call this tool, report `started`
254
260
  or `reused`, and return the canonical clickable URL. It MAY use an available
@@ -510,6 +516,6 @@ closed.
510
516
  Installing or replacing plugin files MUST NOT be described as activating the
511
517
  new MCP code. Release verification MUST install the plugin, activate or reload
512
518
  the new MCP process, ensure the dashboard, and verify the expected TaskChef
513
- version, dashboard protocol `serverVersion`, canonical workspace, and canonical
514
- URL. Exact-compatible listener reuse remains valid; installation MUST NOT be
519
+ version, dashboard protocol `serverVersion`, `mcp` launcher, canonical workspace,
520
+ and canonical URL. Exact-compatible MCP-listener reuse remains valid; installation MUST NOT be
515
521
  claimed to reload Codex automatically.
package/docs/workflows.md CHANGED
@@ -40,11 +40,11 @@ sequenceDiagram
40
40
  participant H as Loopback health
41
41
  participant S as Dashboard server
42
42
  M->>M: Read dashboard.autostart (absent means true)
43
- M->>M: Best-effort ensure after MCP connect
43
+ M->>M: Best-effort ensure before MCP transport connects
44
44
  D->>M: ensure_dashboard()
45
45
  M->>M: Serialize concurrent ensure calls
46
46
  M->>H: GET 127.0.0.1:3210/api/health
47
- alt Exact service, versions, and canonical workspace
47
+ alt Exact service, versions, canonical workspace, and MCP launcher
48
48
  H-->>M: Bounded compatible identity
49
49
  M-->>D: reused, URL, workspace, versions
50
50
  else No listener
@@ -52,7 +52,7 @@ sequenceDiagram
52
52
  M->>S: Start in this MCP process on 127.0.0.1:3210
53
53
  S-->>M: Owned server
54
54
  M-->>D: started, URL, workspace, versions
55
- else Unknown, stale, or different workspace
55
+ else Standalone, unknown, stale, or different workspace
56
56
  H-->>M: Missing or incompatible identity
57
57
  M-->>D: Actionable conflict, listener untouched
58
58
  end
@@ -62,9 +62,10 @@ sequenceDiagram
62
62
  ```
63
63
 
64
64
  When the MCP transport or plugin process closes, it closes only the server it
65
- started. A compatible foreground `taskchef dashboard` listener may be reused
66
- but remains owned by that CLI process. No TaskChef path terminates an unknown
67
- listener or installs OS persistence.
65
+ started. A foreground `taskchef dashboard` listener identifies itself as
66
+ standalone and is never reused as the canonical MCP dashboard, because its
67
+ archive child process may inherit a different host environment. No TaskChef
68
+ path terminates an incompatible listener or installs OS persistence.
68
69
 
69
70
  Autostart and explicit ensures share the same manager promise, so concurrent
70
71
  initialization and recovery calls produce at most one owned listener. An
@@ -79,13 +80,13 @@ The practical release handoff ends in this order:
79
80
  1. Install the released plugin.
80
81
  2. Activate or reload its new TaskChef MCP process.
81
82
  3. Run `$taskchef-dashboard` or call `ensure_dashboard`.
82
- 4. Verify the expected TaskChef version, protocol `serverVersion`, canonical
83
- workspace, and canonical URL returned by the dashboard identity.
83
+ 4. Verify the expected TaskChef version, protocol `serverVersion`, `mcp`
84
+ launcher, canonical workspace, and canonical URL returned by the dashboard identity.
84
85
 
85
86
  Replacing plugin files alone cannot execute autostart because old code remains
86
87
  in the already-running MCP process. Installation does not necessarily reload
87
- Codex. An exact-compatible dashboard may be reused; an unknown listener is
88
- never terminated or replaced.
88
+ Codex. An exact-compatible MCP dashboard may be reused; a standalone or unknown
89
+ listener is never terminated or replaced.
89
90
 
90
91
  ## Normal delegation and self-linking
91
92
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "7.19.0",
3
+ "version": "7.19.1",
4
4
  "description": "A non-blocking interactive dispatcher for visible Codex tasks.",
5
5
  "license": "MIT",
6
6
  "author": "Favo Yang",
@@ -14,13 +14,14 @@ const DEFAULT_HOST = "127.0.0.1";
14
14
  const DEFAULT_PORT = 3210;
15
15
  const HEALTH_TIMEOUT_MS = 750;
16
16
 
17
- function expectedIdentity(workspace, taskchefVersion, serverVersion) {
17
+ function expectedIdentity(workspace, taskchefVersion, serverVersion, launcher) {
18
18
  return {
19
19
  schemaVersion: 1,
20
20
  service: "taskchef-dashboard",
21
21
  taskchefVersion,
22
22
  serverVersion,
23
23
  workspace,
24
+ launcher,
24
25
  };
25
26
  }
26
27
 
@@ -107,6 +108,7 @@ export function createDashboardManager({
107
108
  port = DEFAULT_PORT,
108
109
  taskchefVersion = TASKCHEF_VERSION,
109
110
  serverVersion = DASHBOARD_SERVER_VERSION,
111
+ launcher = "mcp",
110
112
  createServer = createDashboardServer,
111
113
  readIdentity = readDashboardIdentity,
112
114
  } = {}) {
@@ -117,6 +119,7 @@ export function createDashboardManager({
117
119
 
118
120
  const publicResult = (action) => ({
119
121
  action,
122
+ launcher,
120
123
  url: `http://${dashboardAuthority(host, ownedServer?.port ?? port)}/`,
121
124
  workspace: canonicalWorkspace,
122
125
  taskchefVersion,
@@ -132,9 +135,12 @@ export function createDashboardManager({
132
135
  if (listenerAbsent(error)) return false;
133
136
  throw listenerConflict(url, `is occupied but did not return a compatible identity (${error.message}).`);
134
137
  }
135
- const expected = expectedIdentity(canonicalWorkspace, taskchefVersion, serverVersion);
138
+ const expected = expectedIdentity(canonicalWorkspace, taskchefVersion, serverVersion, launcher);
136
139
  if (!isExactIdentity(identity, expected)) {
137
- throw listenerConflict(url, "belongs to an unknown, stale, or different-workspace service.");
140
+ throw listenerConflict(
141
+ url,
142
+ "belongs to an unknown, stale, different-workspace, or differently launched service.",
143
+ );
138
144
  }
139
145
  return true;
140
146
  };
@@ -150,6 +156,7 @@ export function createDashboardManager({
150
156
  port,
151
157
  taskchefVersion,
152
158
  serverVersion,
159
+ launcher,
153
160
  });
154
161
  return publicResult("started");
155
162
  } catch (error) {
package/src/dashboard.js CHANGED
@@ -452,6 +452,7 @@ export async function createDashboardServer({
452
452
  monitorOptions = {},
453
453
  openProject = null,
454
454
  openThread = null,
455
+ launcher = "standalone",
455
456
  taskchefVersion = TASKCHEF_VERSION,
456
457
  serverVersion = DASHBOARD_SERVER_VERSION,
457
458
  } = {}) {
@@ -464,6 +465,9 @@ export async function createDashboardServer({
464
465
  if (!Number.isInteger(maxEventClients) || maxEventClients < 0) {
465
466
  throw new Error("dashboard event-client limit must be a non-negative integer");
466
467
  }
468
+ if (!new Set(["mcp", "standalone"]).has(launcher)) {
469
+ throw new Error("dashboard launcher must be mcp or standalone");
470
+ }
467
471
  const monitor = new DashboardMonitor(workspace, monitorOptions);
468
472
  await monitor.start();
469
473
  const identity = Object.freeze({
@@ -472,6 +476,7 @@ export async function createDashboardServer({
472
476
  taskchefVersion,
473
477
  serverVersion,
474
478
  workspace: monitor.workspace,
479
+ launcher,
475
480
  });
476
481
  if (Buffer.byteLength(`${JSON.stringify(identity)}\n`) > DASHBOARD_HEALTH_MAX_BYTES) {
477
482
  monitor.close();
package/src/mcp.js CHANGED
@@ -88,6 +88,7 @@ const preparationSchema = z.object({
88
88
 
89
89
  const dashboardSchema = z.object({
90
90
  action: z.enum(["started", "reused"]),
91
+ launcher: z.literal("mcp"),
91
92
  url: z.string().url(),
92
93
  workspace: z.string(),
93
94
  taskchefVersion: z.string(),
@@ -163,7 +164,7 @@ export function createTaskChefMcpServer({
163
164
  return closePromise;
164
165
  };
165
166
  server.server.onclose = () => {
166
- void dashboardManager.close();
167
+ void dashboardManager.close().catch(() => {});
167
168
  };
168
169
 
169
170
  server.registerTool(
@@ -171,7 +172,7 @@ export function createTaskChefMcpServer({
171
172
  {
172
173
  title: "Ensure TaskChef dashboard",
173
174
  description:
174
- "Best-effort ensure the canonical TaskChef dashboard is available on 127.0.0.1:3210. Starts one dashboard inside this MCP process or reuses only an exact compatible TaskChef dashboard for the same canonical workspace; unknown listeners are never terminated or replaced.",
175
+ "Best-effort ensure the canonical TaskChef dashboard is available on 127.0.0.1:3210. Starts one dashboard inside this MCP process or reuses only an exact compatible MCP-launched TaskChef dashboard for the same canonical workspace; standalone and unknown listeners are never terminated or replaced.",
175
176
  inputSchema: {},
176
177
  outputSchema: { dashboard: dashboardSchema },
177
178
  annotations: {
@@ -325,8 +326,16 @@ export function createTaskChefMcpServer({
325
326
  ...(logDashboardDiagnostic ? { log: logDashboardDiagnostic } : {}),
326
327
  });
327
328
  server.connect = async (...args) => {
328
- await originalConnect(...args);
329
- void autostartDashboard();
329
+ await autostartDashboard();
330
+ try {
331
+ await originalConnect(...args);
332
+ } catch (error) {
333
+ await Promise.allSettled([
334
+ Promise.resolve().then(() => dashboardManager.close()),
335
+ Promise.resolve().then(() => originalClose()),
336
+ ]);
337
+ throw error;
338
+ }
330
339
  };
331
340
 
332
341
  return server;
package/src/version.js CHANGED
@@ -4,4 +4,4 @@ const require = createRequire(import.meta.url);
4
4
  const packageMetadata = require("../package.json");
5
5
 
6
6
  export const TASKCHEF_VERSION = packageMetadata.version;
7
- export const DASHBOARD_SERVER_VERSION = "2";
7
+ export const DASHBOARD_SERVER_VERSION = "3";