agent-conveyor 0.1.8 → 0.1.9
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 +20 -4
- package/dist/cli/typescript-runtime.js +290 -7
- package/dist/cli/typescript-runtime.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime/app-autonomy.d.ts +109 -0
- package/dist/runtime/app-autonomy.js +263 -0
- package/dist/runtime/app-autonomy.js.map +1 -0
- package/docs/manager-recipes.md +36 -6
- package/package.json +1 -1
- package/skills/manage-codex-workers/SKILL.md +41 -24
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export { configureConnectionSync, databaseHealthSync, initializeDatabaseSync, op
|
|
|
7
7
|
export type { DatabaseCheck, DatabaseHealth } from "./state/database.js";
|
|
8
8
|
export { taskAuditSync, TaskAuditError } from "./runtime/audit.js";
|
|
9
9
|
export type { TaskAuditCorrelationChain, TaskAuditEvent, TaskAuditManagerDecision, TaskAuditResult, TaskAuditRoutedNotification, TaskAuditTask, } from "./runtime/audit.js";
|
|
10
|
+
export { appHeartbeatPollCommand, appLoopStatusCommand, appLoopStatusSync, appWakeupDispatchPlanSync, appWakeupPlanCommand, appWakeupPlanSync, directInboxPollCommand, } from "./runtime/app-autonomy.js";
|
|
11
|
+
export type { AppLoopDispatchState, AppLoopLeaseState, AppLoopNextAction, AppLoopRole, AppLoopRoleStatus, AppLoopStatus, AppWakeup, AppWakeupDispatchAction, AppWakeupDispatchActionStatus, AppWakeupDispatchPlan, AppWakeupPlan, } from "./runtime/app-autonomy.js";
|
|
10
12
|
export { exportTaskAuditSubsetSync, exportTaskSync, TaskExportError } from "./runtime/export.js";
|
|
11
13
|
export type { TaskExportManifest, TaskExportResult } from "./runtime/export.js";
|
|
12
14
|
export { replayEntriesFromAudit, replayResultFromAudit } from "./runtime/replay.js";
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export { captureMetaPath, configPath, defaultDbPath, eventsPath, loadJsonSync, s
|
|
|
3
3
|
export { latestStatusSync } from "./state/status.js";
|
|
4
4
|
export { configureConnectionSync, databaseHealthSync, initializeDatabaseSync, openDatabaseSync, WorkerctlDatabaseError, } from "./state/database.js";
|
|
5
5
|
export { taskAuditSync, TaskAuditError } from "./runtime/audit.js";
|
|
6
|
+
export { appHeartbeatPollCommand, appLoopStatusCommand, appLoopStatusSync, appWakeupDispatchPlanSync, appWakeupPlanCommand, appWakeupPlanSync, directInboxPollCommand, } from "./runtime/app-autonomy.js";
|
|
6
7
|
export { exportTaskAuditSubsetSync, exportTaskSync, TaskExportError } from "./runtime/export.js";
|
|
7
8
|
export { replayEntriesFromAudit, replayResultFromAudit } from "./runtime/replay.js";
|
|
8
9
|
export { classifyBusyWait, classifyStartupOutput } from "./runtime/classify.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EACL,eAAe,EACf,UAAU,EACV,aAAa,EACb,UAAU,EACV,YAAY,EACZ,SAAS,EACT,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,SAAS,EACT,mBAAmB,EACnB,aAAa,GACd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AASnE,OAAO,EAAE,yBAAyB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEjG,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEpF,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEhF,OAAO,EACL,6BAA6B,EAC7B,4BAA4B,EAC5B,iBAAiB,EACjB,wBAAwB,EACxB,uCAAuC,EACvC,8BAA8B,EAC9B,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,mCAAmC,EACnC,0BAA0B,EAC1B,+BAA+B,EAC/B,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,8BAA8B,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhG,OAAO,EACL,+BAA+B,EAC/B,yBAAyB,EACzB,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACL,+BAA+B,EAC/B,2CAA2C,EAC3C,gCAAgC,EAChC,4BAA4B,EAC5B,4BAA4B,EAC5B,2CAA2C,EAC3C,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAOpC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAEhJ,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAEjH,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,iCAAiC,EACjC,sBAAsB,EACtB,gCAAgC,EAChC,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AASpC,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhG,OAAO,EACL,wBAAwB,EACxB,gBAAgB,EAChB,cAAc,EACd,+BAA+B,EAC/B,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,aAAa,EACb,6BAA6B,EAC7B,uBAAuB,EACvB,2BAA2B,EAC3B,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,yBAAyB,EACzB,0BAA0B,EAC1B,WAAW,EACX,kBAAkB,EAClB,UAAU,GACX,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,EACL,eAAe,EACf,UAAU,EACV,aAAa,EACb,UAAU,EACV,YAAY,EACZ,SAAS,EACT,UAAU,EACV,cAAc,EACd,kBAAkB,EAClB,SAAS,EACT,mBAAmB,EACnB,aAAa,GACd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AASnE,OAAO,EACL,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AAcnC,OAAO,EAAE,yBAAyB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEjG,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEpF,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEhF,OAAO,EACL,6BAA6B,EAC7B,4BAA4B,EAC5B,iBAAiB,EACjB,wBAAwB,EACxB,uCAAuC,EACvC,8BAA8B,EAC9B,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,mCAAmC,EACnC,0BAA0B,EAC1B,+BAA+B,EAC/B,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,8BAA8B,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhG,OAAO,EACL,+BAA+B,EAC/B,yBAAyB,EACzB,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EACL,+BAA+B,EAC/B,2CAA2C,EAC3C,gCAAgC,EAChC,4BAA4B,EAC5B,4BAA4B,EAC5B,2CAA2C,EAC3C,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AAOpC,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAEhJ,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAEjH,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,iCAAiC,EACjC,sBAAsB,EACtB,gCAAgC,EAChC,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AASpC,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhG,OAAO,EACL,wBAAwB,EACxB,gBAAgB,EAChB,cAAc,EACd,+BAA+B,EAC/B,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,aAAa,EACb,6BAA6B,EAC7B,uBAAuB,EACvB,2BAA2B,EAC3B,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,yBAAyB,EACzB,0BAA0B,EAC1B,WAAW,EACX,kBAAkB,EAClB,UAAU,GACX,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { DatabaseSync } from "node:sqlite";
|
|
2
|
+
export type AppLoopLeaseState = "healthy" | "missing" | "stale";
|
|
3
|
+
export type AppLoopDispatchState = "healthy" | "missing" | "stale";
|
|
4
|
+
export type AppLoopRole = "manager" | "worker";
|
|
5
|
+
export interface AppLoopRoleStatus {
|
|
6
|
+
codex_app_thread_id: string | null;
|
|
7
|
+
codex_app_thread_title: string | null;
|
|
8
|
+
direct_inbox_command: string | null;
|
|
9
|
+
lease: {
|
|
10
|
+
age_seconds: number | null;
|
|
11
|
+
last_heartbeat_at: string | null;
|
|
12
|
+
stale_after_seconds: number;
|
|
13
|
+
state: AppLoopLeaseState;
|
|
14
|
+
};
|
|
15
|
+
name: string | null;
|
|
16
|
+
poll_command: string | null;
|
|
17
|
+
receive_style: "pull" | "push" | null;
|
|
18
|
+
session_id: string | null;
|
|
19
|
+
session_kind: "codex_app" | "no_tmux" | "tmux" | null;
|
|
20
|
+
}
|
|
21
|
+
export interface AppLoopNextAction {
|
|
22
|
+
kind: "start_dispatch" | "wake_manager" | "wake_worker";
|
|
23
|
+
prompt?: string;
|
|
24
|
+
reason: string;
|
|
25
|
+
role?: AppLoopRole;
|
|
26
|
+
}
|
|
27
|
+
export interface AppLoopStatus {
|
|
28
|
+
dispatch: {
|
|
29
|
+
dispatcher_id: string;
|
|
30
|
+
last_heartbeat_at: string | null;
|
|
31
|
+
state: AppLoopDispatchState;
|
|
32
|
+
};
|
|
33
|
+
manager: AppLoopRoleStatus;
|
|
34
|
+
next_actions: AppLoopNextAction[];
|
|
35
|
+
ok: boolean;
|
|
36
|
+
task: {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
};
|
|
40
|
+
worker: AppLoopRoleStatus;
|
|
41
|
+
}
|
|
42
|
+
export interface AppWakeupPlan {
|
|
43
|
+
dispatcher: {
|
|
44
|
+
command: string;
|
|
45
|
+
required: boolean;
|
|
46
|
+
state: AppLoopDispatchState;
|
|
47
|
+
};
|
|
48
|
+
status: AppLoopStatus;
|
|
49
|
+
wakeups: AppWakeup[];
|
|
50
|
+
}
|
|
51
|
+
export interface AppWakeup {
|
|
52
|
+
prompt: string;
|
|
53
|
+
reason: string;
|
|
54
|
+
role: AppLoopRole;
|
|
55
|
+
thread: {
|
|
56
|
+
id: string | null;
|
|
57
|
+
title: string | null;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export type AppWakeupDispatchActionStatus = "blocked_missing_thread" | "ready_to_send" | "skipped_healthy";
|
|
61
|
+
export interface AppWakeupDispatchAction {
|
|
62
|
+
blocker: string | null;
|
|
63
|
+
prompt: string | null;
|
|
64
|
+
reason: string;
|
|
65
|
+
role: AppLoopRole;
|
|
66
|
+
send_ready: boolean;
|
|
67
|
+
status: AppWakeupDispatchActionStatus;
|
|
68
|
+
thread: {
|
|
69
|
+
id: string | null;
|
|
70
|
+
title: string | null;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export interface AppWakeupDispatchPlan {
|
|
74
|
+
actions: AppWakeupDispatchAction[];
|
|
75
|
+
dispatcher: AppWakeupPlan["dispatcher"];
|
|
76
|
+
status: AppLoopStatus;
|
|
77
|
+
summary: {
|
|
78
|
+
blocked: number;
|
|
79
|
+
dispatcher_required: boolean;
|
|
80
|
+
ready_to_send: number;
|
|
81
|
+
skipped: number;
|
|
82
|
+
total_actions: number;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export declare function appLoopStatusSync(database: DatabaseSync, options: {
|
|
86
|
+
dbPath?: string | null;
|
|
87
|
+
dispatcherId: string;
|
|
88
|
+
heartbeatStaleSeconds: number;
|
|
89
|
+
now: string;
|
|
90
|
+
taskName: string;
|
|
91
|
+
}): AppLoopStatus;
|
|
92
|
+
export declare function appWakeupPlanSync(database: DatabaseSync, options: {
|
|
93
|
+
dbPath?: string | null;
|
|
94
|
+
dispatcherId: string;
|
|
95
|
+
heartbeatStaleSeconds: number;
|
|
96
|
+
now: string;
|
|
97
|
+
taskName: string;
|
|
98
|
+
}): AppWakeupPlan;
|
|
99
|
+
export declare function appWakeupDispatchPlanSync(database: DatabaseSync, options: {
|
|
100
|
+
dbPath?: string | null;
|
|
101
|
+
dispatcherId: string;
|
|
102
|
+
heartbeatStaleSeconds: number;
|
|
103
|
+
now: string;
|
|
104
|
+
taskName: string;
|
|
105
|
+
}): AppWakeupDispatchPlan;
|
|
106
|
+
export declare function appHeartbeatPollCommand(role: AppLoopRole, taskName: string, dbPath?: string | null): string;
|
|
107
|
+
export declare function appLoopStatusCommand(taskName: string, dbPath?: string | null): string;
|
|
108
|
+
export declare function appWakeupPlanCommand(taskName: string, dbPath?: string | null): string;
|
|
109
|
+
export declare function directInboxPollCommand(role: AppLoopRole, taskName: string, dbPath?: string | null): string;
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
export function appLoopStatusSync(database, options) {
|
|
2
|
+
const task = database.prepare("select id, name from tasks where name = ?").get(options.taskName);
|
|
3
|
+
if (!task) {
|
|
4
|
+
throw new Error(`Task not found: ${options.taskName}`);
|
|
5
|
+
}
|
|
6
|
+
const binding = database.prepare(`
|
|
7
|
+
select ws.id as worker_session_id,
|
|
8
|
+
ws.name as worker_name,
|
|
9
|
+
ws.last_heartbeat_at as worker_last_heartbeat_at,
|
|
10
|
+
ws.codex_app_thread_id as worker_thread_id,
|
|
11
|
+
ws.codex_app_thread_title as worker_thread_title,
|
|
12
|
+
ws.tmux_session as worker_tmux_session,
|
|
13
|
+
ms.id as manager_session_id,
|
|
14
|
+
ms.name as manager_name,
|
|
15
|
+
ms.last_heartbeat_at as manager_last_heartbeat_at,
|
|
16
|
+
ms.codex_app_thread_id as manager_thread_id,
|
|
17
|
+
ms.codex_app_thread_title as manager_thread_title,
|
|
18
|
+
ms.tmux_session as manager_tmux_session
|
|
19
|
+
from bindings
|
|
20
|
+
left join sessions ws on ws.id = bindings.worker_session_id
|
|
21
|
+
left join sessions ms on ms.id = bindings.manager_session_id
|
|
22
|
+
where bindings.task_id = ? and bindings.state in ('active', 'ending')
|
|
23
|
+
order by bindings.created_at desc
|
|
24
|
+
limit 1
|
|
25
|
+
`).get(task.id);
|
|
26
|
+
if (!binding) {
|
|
27
|
+
throw new Error(`No active binding for task: ${options.taskName}`);
|
|
28
|
+
}
|
|
29
|
+
const dispatchHeartbeat = database.prepare(`
|
|
30
|
+
select timestamp
|
|
31
|
+
from telemetry_events
|
|
32
|
+
where actor = 'dispatch'
|
|
33
|
+
and event_type = 'dispatch_watch_heartbeat'
|
|
34
|
+
and json_extract(correlation_json, '$.dispatcher_id') = ?
|
|
35
|
+
order by timestamp desc, id desc
|
|
36
|
+
limit 1
|
|
37
|
+
`).get(options.dispatcherId);
|
|
38
|
+
const manager = roleStatus({
|
|
39
|
+
dbPath: options.dbPath ?? null,
|
|
40
|
+
heartbeatStaleSeconds: options.heartbeatStaleSeconds,
|
|
41
|
+
lastHeartbeatAt: binding.manager_last_heartbeat_at,
|
|
42
|
+
name: binding.manager_name,
|
|
43
|
+
now: options.now,
|
|
44
|
+
role: "manager",
|
|
45
|
+
sessionId: binding.manager_session_id,
|
|
46
|
+
taskName: options.taskName,
|
|
47
|
+
threadId: binding.manager_thread_id,
|
|
48
|
+
threadTitle: binding.manager_thread_title,
|
|
49
|
+
tmuxSession: binding.manager_tmux_session,
|
|
50
|
+
});
|
|
51
|
+
const worker = roleStatus({
|
|
52
|
+
dbPath: options.dbPath ?? null,
|
|
53
|
+
heartbeatStaleSeconds: options.heartbeatStaleSeconds,
|
|
54
|
+
lastHeartbeatAt: binding.worker_last_heartbeat_at,
|
|
55
|
+
name: binding.worker_name,
|
|
56
|
+
now: options.now,
|
|
57
|
+
role: "worker",
|
|
58
|
+
sessionId: binding.worker_session_id,
|
|
59
|
+
taskName: options.taskName,
|
|
60
|
+
threadId: binding.worker_thread_id,
|
|
61
|
+
threadTitle: binding.worker_thread_title,
|
|
62
|
+
tmuxSession: binding.worker_tmux_session,
|
|
63
|
+
});
|
|
64
|
+
const dispatchState = dispatchLeaseState(dispatchHeartbeat?.timestamp ?? null, options.now, options.heartbeatStaleSeconds);
|
|
65
|
+
const dispatch = {
|
|
66
|
+
dispatcher_id: options.dispatcherId,
|
|
67
|
+
last_heartbeat_at: dispatchHeartbeat?.timestamp ?? null,
|
|
68
|
+
state: dispatchState,
|
|
69
|
+
};
|
|
70
|
+
const nextActions = [];
|
|
71
|
+
if (dispatch.state !== "healthy") {
|
|
72
|
+
nextActions.push({
|
|
73
|
+
kind: "start_dispatch",
|
|
74
|
+
prompt: `Run: conveyor dispatch --watch --dispatcher-id ${shellQuote(options.dispatcherId)}${pathFlag(options.dbPath ?? null)}`,
|
|
75
|
+
reason: `Dispatch ${options.dispatcherId} is ${dispatch.state}.`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (manager.lease.state !== "healthy") {
|
|
79
|
+
nextActions.push({
|
|
80
|
+
kind: "wake_manager",
|
|
81
|
+
prompt: manager.poll_command ?? undefined,
|
|
82
|
+
reason: `Manager heartbeat is ${manager.lease.state}.`,
|
|
83
|
+
role: "manager",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (worker.lease.state !== "healthy") {
|
|
87
|
+
nextActions.push({
|
|
88
|
+
kind: "wake_worker",
|
|
89
|
+
prompt: worker.poll_command ?? undefined,
|
|
90
|
+
reason: `Worker heartbeat is ${worker.lease.state}.`,
|
|
91
|
+
role: "worker",
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
dispatch,
|
|
96
|
+
manager,
|
|
97
|
+
next_actions: nextActions,
|
|
98
|
+
ok: dispatch.state === "healthy" && manager.lease.state === "healthy" && worker.lease.state === "healthy",
|
|
99
|
+
task,
|
|
100
|
+
worker,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export function appWakeupPlanSync(database, options) {
|
|
104
|
+
const status = appLoopStatusSync(database, options);
|
|
105
|
+
const wakeups = [];
|
|
106
|
+
if (status.manager.lease.state !== "healthy") {
|
|
107
|
+
wakeups.push(roleWakeup("manager", status.manager, status.task.name, options.dbPath ?? null, status.manager.lease.state));
|
|
108
|
+
}
|
|
109
|
+
if (status.worker.lease.state !== "healthy") {
|
|
110
|
+
wakeups.push(roleWakeup("worker", status.worker, status.task.name, options.dbPath ?? null, status.worker.lease.state));
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
dispatcher: {
|
|
114
|
+
command: `conveyor dispatch --watch --dispatcher-id ${shellQuote(options.dispatcherId)}${pathFlag(options.dbPath ?? null)}`,
|
|
115
|
+
required: status.dispatch.state !== "healthy",
|
|
116
|
+
state: status.dispatch.state,
|
|
117
|
+
},
|
|
118
|
+
status,
|
|
119
|
+
wakeups,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function appWakeupDispatchPlanSync(database, options) {
|
|
123
|
+
const plan = appWakeupPlanSync(database, options);
|
|
124
|
+
const wakeupsByRole = new Map(plan.wakeups.map((wakeup) => [wakeup.role, wakeup]));
|
|
125
|
+
const actions = ["manager", "worker"].map((role) => {
|
|
126
|
+
const roleStatus = role === "manager" ? plan.status.manager : plan.status.worker;
|
|
127
|
+
const wakeup = wakeupsByRole.get(role);
|
|
128
|
+
if (!wakeup) {
|
|
129
|
+
return {
|
|
130
|
+
blocker: null,
|
|
131
|
+
prompt: null,
|
|
132
|
+
reason: `${role} heartbeat is healthy.`,
|
|
133
|
+
role,
|
|
134
|
+
send_ready: false,
|
|
135
|
+
status: "skipped_healthy",
|
|
136
|
+
thread: {
|
|
137
|
+
id: roleStatus.codex_app_thread_id,
|
|
138
|
+
title: roleStatus.codex_app_thread_title,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (!wakeup.thread.id) {
|
|
143
|
+
return {
|
|
144
|
+
blocker: `No Codex app thread id is registered for the ${role}; use the prompt manually or register app thread metadata before calling send_message_to_thread.`,
|
|
145
|
+
prompt: wakeup.prompt,
|
|
146
|
+
reason: wakeup.reason,
|
|
147
|
+
role,
|
|
148
|
+
send_ready: false,
|
|
149
|
+
status: "blocked_missing_thread",
|
|
150
|
+
thread: wakeup.thread,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
blocker: null,
|
|
155
|
+
prompt: wakeup.prompt,
|
|
156
|
+
reason: wakeup.reason,
|
|
157
|
+
role,
|
|
158
|
+
send_ready: true,
|
|
159
|
+
status: "ready_to_send",
|
|
160
|
+
thread: wakeup.thread,
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
const ready = actions.filter((action) => action.status === "ready_to_send").length;
|
|
164
|
+
const blocked = actions.filter((action) => action.status === "blocked_missing_thread").length;
|
|
165
|
+
const skipped = actions.filter((action) => action.status === "skipped_healthy").length;
|
|
166
|
+
return {
|
|
167
|
+
actions,
|
|
168
|
+
dispatcher: plan.dispatcher,
|
|
169
|
+
status: plan.status,
|
|
170
|
+
summary: {
|
|
171
|
+
blocked,
|
|
172
|
+
dispatcher_required: plan.dispatcher.required,
|
|
173
|
+
ready_to_send: ready,
|
|
174
|
+
skipped,
|
|
175
|
+
total_actions: actions.length,
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
export function appHeartbeatPollCommand(role, taskName, dbPath) {
|
|
180
|
+
return `conveyor app-heartbeat ${shellQuote(taskName)} --role ${role}${pathFlag(dbPath ?? null)} --json`;
|
|
181
|
+
}
|
|
182
|
+
export function appLoopStatusCommand(taskName, dbPath) {
|
|
183
|
+
return `conveyor app-loop-status ${shellQuote(taskName)}${pathFlag(dbPath ?? null)} --json`;
|
|
184
|
+
}
|
|
185
|
+
export function appWakeupPlanCommand(taskName, dbPath) {
|
|
186
|
+
return `conveyor app-wakeup-plan ${shellQuote(taskName)}${pathFlag(dbPath ?? null)} --json`;
|
|
187
|
+
}
|
|
188
|
+
export function directInboxPollCommand(role, taskName, dbPath) {
|
|
189
|
+
const inbox = role === "manager" ? "manager-inbox" : "worker-inbox";
|
|
190
|
+
return `conveyor ${inbox} ${shellQuote(taskName)} --consume-next --wait --timeout 60${pathFlag(dbPath ?? null)} --json`;
|
|
191
|
+
}
|
|
192
|
+
function roleStatus(options) {
|
|
193
|
+
const hasTmux = Boolean(options.tmuxSession);
|
|
194
|
+
return {
|
|
195
|
+
codex_app_thread_id: options.threadId,
|
|
196
|
+
codex_app_thread_title: options.threadTitle,
|
|
197
|
+
direct_inbox_command: directInboxPollCommand(options.role, options.taskName, options.dbPath),
|
|
198
|
+
lease: {
|
|
199
|
+
age_seconds: ageSeconds(options.lastHeartbeatAt, options.now),
|
|
200
|
+
last_heartbeat_at: options.lastHeartbeatAt,
|
|
201
|
+
stale_after_seconds: options.heartbeatStaleSeconds,
|
|
202
|
+
state: leaseState(options.lastHeartbeatAt, options.now, options.heartbeatStaleSeconds),
|
|
203
|
+
},
|
|
204
|
+
name: options.name,
|
|
205
|
+
poll_command: appHeartbeatPollCommand(options.role, options.taskName, options.dbPath),
|
|
206
|
+
receive_style: hasTmux ? "push" : "pull",
|
|
207
|
+
session_id: options.sessionId,
|
|
208
|
+
session_kind: hasTmux ? "tmux" : options.threadId ? "codex_app" : "no_tmux",
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function roleWakeup(role, status, taskName, dbPath, state) {
|
|
212
|
+
const pollCommand = appHeartbeatPollCommand(role, taskName, dbPath);
|
|
213
|
+
const directInboxCommand = directInboxPollCommand(role, taskName, dbPath);
|
|
214
|
+
const roleInstruction = role === "manager"
|
|
215
|
+
? "If an item is consumed, verify worker claims before recording conclusions, require concrete evidence, update Conveyor state as appropriate, and produce exactly one next worker task."
|
|
216
|
+
: "If an item is consumed, execute only that single worker instruction and return exact commands, compact evidence, blockers or residual risk, and exactly one next recommended worker task.";
|
|
217
|
+
return {
|
|
218
|
+
prompt: [
|
|
219
|
+
"Use the manage-codex-workers skill.",
|
|
220
|
+
`This is the ${role} heartbeat wakeup for task ${taskName}.`,
|
|
221
|
+
`Run: ${pollCommand}`,
|
|
222
|
+
`If the heartbeat result asks for direct inbox polling, run: ${directInboxCommand}`,
|
|
223
|
+
roleInstruction,
|
|
224
|
+
"If no item is consumed, stop after a one-line idle receipt.",
|
|
225
|
+
"Idle polling is not completion and does not authorize heartbeat teardown.",
|
|
226
|
+
].join("\n"),
|
|
227
|
+
reason: `${role} heartbeat is ${state}.`,
|
|
228
|
+
role,
|
|
229
|
+
thread: {
|
|
230
|
+
id: status.codex_app_thread_id,
|
|
231
|
+
title: status.codex_app_thread_title,
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function dispatchLeaseState(value, now, staleSeconds) {
|
|
236
|
+
const state = leaseState(value, now, staleSeconds);
|
|
237
|
+
return state === "healthy" ? "healthy" : state === "stale" ? "stale" : "missing";
|
|
238
|
+
}
|
|
239
|
+
function leaseState(value, now, staleSeconds) {
|
|
240
|
+
if (!value) {
|
|
241
|
+
return "missing";
|
|
242
|
+
}
|
|
243
|
+
const age = ageSeconds(value, now);
|
|
244
|
+
return age !== null && age <= staleSeconds ? "healthy" : "stale";
|
|
245
|
+
}
|
|
246
|
+
function ageSeconds(value, now) {
|
|
247
|
+
if (!value) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
const parsedNow = Date.parse(now);
|
|
251
|
+
const parsedValue = Date.parse(value);
|
|
252
|
+
if (!Number.isFinite(parsedNow) || !Number.isFinite(parsedValue)) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
return Math.max(0, Math.floor((parsedNow - parsedValue) / 1000));
|
|
256
|
+
}
|
|
257
|
+
function pathFlag(dbPath) {
|
|
258
|
+
return dbPath ? ` --path ${shellQuote(dbPath)}` : "";
|
|
259
|
+
}
|
|
260
|
+
function shellQuote(value) {
|
|
261
|
+
return `'${value.replace(/'/g, "'\"'\"'")}'`;
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=app-autonomy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-autonomy.js","sourceRoot":"","sources":["../../src/runtime/app-autonomy.ts"],"names":[],"mappings":"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"}
|
package/docs/manager-recipes.md
CHANGED
|
@@ -273,17 +273,47 @@ report heartbeat teardown status. If the task or binding still appears active,
|
|
|
273
273
|
report that as a control-plane blocker instead of calling the loop complete.
|
|
274
274
|
|
|
275
275
|
When the manager is running in the Codex app and thread tools are available,
|
|
276
|
-
create
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
`--
|
|
280
|
-
`send_message_to_thread`
|
|
281
|
-
|
|
276
|
+
create fresh same-project manager and worker threads with `create_thread`, set
|
|
277
|
+
readable titles, and pass both ids/titles into `conveyor
|
|
278
|
+
create-disposable-binding` with `--manager-codex-app-thread-id`,
|
|
279
|
+
`--manager-codex-app-thread-title`, `--worker-codex-app-thread-id`, and
|
|
280
|
+
`--worker-codex-app-thread-title`. Use `send_message_to_thread` for bootstrap
|
|
281
|
+
prompts only; ongoing manager/worker communication should still be routed
|
|
282
282
|
through Dispatch and consumed from inboxes. If app thread tools are unavailable,
|
|
283
283
|
open the worker session manually and paste the same `worker_handoff` prompt.
|
|
284
284
|
Do not use `fork_thread` for this recipe unless the user explicitly wants a
|
|
285
285
|
fork of the current conversation.
|
|
286
286
|
|
|
287
|
+
### Codex App Native Manager/Worker Loop
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
conveyor doctor
|
|
291
|
+
conveyor db-doctor
|
|
292
|
+
conveyor create-disposable-binding "$TASK" \
|
|
293
|
+
--worker "$WORKER" \
|
|
294
|
+
--manager "$MANAGER" \
|
|
295
|
+
--worker-codex-app-thread-id "$WORKER_THREAD_ID" \
|
|
296
|
+
--worker-codex-app-thread-title "$WORKER_THREAD_TITLE" \
|
|
297
|
+
--manager-codex-app-thread-id "$MANAGER_THREAD_ID" \
|
|
298
|
+
--manager-codex-app-thread-title "$MANAGER_THREAD_TITLE" \
|
|
299
|
+
--adversarial \
|
|
300
|
+
--json
|
|
301
|
+
conveyor dispatch --watch --dispatcher-id dispatch-local
|
|
302
|
+
conveyor app-heartbeat "$TASK" --role manager --json
|
|
303
|
+
conveyor app-heartbeat "$TASK" --role worker --json
|
|
304
|
+
conveyor app-loop-status "$TASK" --json
|
|
305
|
+
conveyor app-wakeup-plan "$TASK" --json
|
|
306
|
+
conveyor app-wakeup-dispatch "$TASK" --json
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Use `app-loop-status` as the operator status surface. If it reports stale
|
|
310
|
+
manager, worker, or Dispatch leases, use `app-wakeup-plan` to get the exact
|
|
311
|
+
thread prompt to send through Codex app automation or `send_message_to_thread`.
|
|
312
|
+
Use `app-wakeup-dispatch` when the manager needs a durable Conveyor receipt for
|
|
313
|
+
which app wake actions were prepared, skipped, or blocked. It only prepares
|
|
314
|
+
adapter-ready actions; direct app-thread delivery remains an app/operator
|
|
315
|
+
action, and Dispatch/inboxes remain the durable task state.
|
|
316
|
+
|
|
287
317
|
The saved dogfood example is
|
|
288
318
|
`docs/goals/live-codex-app-inbox-drill/notes/T001-live-drill.md`. It proves
|
|
289
319
|
manager-to-worker `nudge_worker` delivery and worker-to-manager
|
package/package.json
CHANGED
|
@@ -35,25 +35,29 @@ Skill behavior:
|
|
|
35
35
|
report blockers.
|
|
36
36
|
3. Choose concise task, worker, manager, and run names when the user does not
|
|
37
37
|
provide them. Do not ask the user to invent generated names.
|
|
38
|
-
4.
|
|
39
|
-
same-project
|
|
40
|
-
`
|
|
41
|
-
`create_thread` for this flow; do not use
|
|
42
|
-
explicitly asks to fork or resume this exact
|
|
38
|
+
4. Preferred fully app-native setup: if running in the Codex app and thread
|
|
39
|
+
tools are available, create fresh same-project manager and worker threads
|
|
40
|
+
with `create_thread`, give both readable titles, and keep both returned
|
|
41
|
+
thread ids/titles. Use `create_thread` for this flow; do not use
|
|
42
|
+
`fork_thread` unless the user explicitly asks to fork or resume this exact
|
|
43
|
+
conversation. If the current thread is explicitly acting as the manager,
|
|
44
|
+
use the current thread as manager and create only the worker thread.
|
|
43
45
|
5. Create the no-tmux binding with `conveyor create-disposable-binding`
|
|
44
46
|
using `--template` when a template is known, `--adversarial`, a bounded
|
|
45
|
-
`--max-iterations`, and `--json`. When step 4 produced
|
|
46
|
-
|
|
47
|
-
`--
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
`--max-iterations`, and `--json`. When step 4 produced app thread ids, pass
|
|
48
|
+
them through `--manager-codex-app-thread-id`,
|
|
49
|
+
`--manager-codex-app-thread-title`, `--worker-codex-app-thread-id`, and
|
|
50
|
+
`--worker-codex-app-thread-title` so Conveyor can surface app identities in
|
|
51
|
+
`sessions`, `discover`, and setup JSON. If app thread tools are not
|
|
52
|
+
available, create the binding without those flags and ask the user to open
|
|
53
|
+
separate Codex app sessions manually.
|
|
51
54
|
6. Ensure Dispatch is running or tell the user the single command to start it:
|
|
52
55
|
`conveyor dispatch --watch --dispatcher-id dispatch-local`.
|
|
53
56
|
7. Read the returned `communication` blocks. A worker or manager with
|
|
54
57
|
`session_kind=tmux` and `receive_style=push` can receive direct tmux pushes;
|
|
55
58
|
one with `session_kind=codex_app` and `receive_style=pull` must poll the
|
|
56
|
-
printed inbox
|
|
59
|
+
printed `app-heartbeat` command, using direct inbox commands only when the
|
|
60
|
+
heartbeat output or setup JSON provides them as fallbacks.
|
|
57
61
|
8. Give the worker Codex app session the generated `worker_handoff` prompt.
|
|
58
62
|
If step 4 created a fresh worker thread, use `send_message_to_thread` only
|
|
59
63
|
to deliver that bootstrap prompt. Durable manager/worker communication still
|
|
@@ -72,10 +76,12 @@ Skill behavior:
|
|
|
72
76
|
Idle polling rule for Codex app/no-tmux sessions:
|
|
73
77
|
|
|
74
78
|
- When a worker has `session_kind=codex_app` or `receive_style=pull`, its
|
|
75
|
-
idle/check-in command is the returned
|
|
76
|
-
`communication.poll_command` or generated `worker_handoff` command
|
|
79
|
+
default idle/check-in command is the returned `app-heartbeat` worker command.
|
|
80
|
+
Use `communication.poll_command` or the generated `worker_handoff` command as
|
|
81
|
+
fallback/direct inbox polling.
|
|
77
82
|
- When a manager has `session_kind=codex_app` or `receive_style=pull`, its
|
|
78
|
-
idle/check-in command is the returned `
|
|
83
|
+
default idle/check-in command is the returned `app-heartbeat` manager command.
|
|
84
|
+
Use `communication.poll_command` as fallback/direct inbox polling.
|
|
79
85
|
- The printed command may include a local `PATH=.../bin:$PATH conveyor` prefix;
|
|
80
86
|
preserve that prefix when giving the command to a Codex app thread.
|
|
81
87
|
- Repeat the appropriate command whenever the session is idle, after finishing
|
|
@@ -83,6 +89,13 @@ Idle polling rule for Codex app/no-tmux sessions:
|
|
|
83
89
|
A timeout is not completion; it is only a quiet poll interval.
|
|
84
90
|
- Keep `conveyor dispatch --watch --dispatcher-id dispatch-local` running so
|
|
85
91
|
Dispatch can route new messages into those inboxes.
|
|
92
|
+
- Use `conveyor app-loop-status <task> --json` as the operator status surface,
|
|
93
|
+
and `conveyor app-wakeup-plan <task> --json` to produce exact stale-thread
|
|
94
|
+
wake prompts for Codex app automation or `send_message_to_thread`.
|
|
95
|
+
- Use `conveyor app-wakeup-dispatch <task> --json` when the manager needs an
|
|
96
|
+
auditable wake orchestration receipt. It prepares adapter-ready app-thread
|
|
97
|
+
actions, reports skipped and blocked roles, and records telemetry; it does
|
|
98
|
+
not send direct app-thread messages itself.
|
|
86
99
|
- For bounded Ralph loops, treat `ralph_loop_iteration_advanced` telemetry as
|
|
87
100
|
the receipt that a worker actually consumed and began the requested
|
|
88
101
|
iteration.
|
|
@@ -727,15 +740,19 @@ etc.) run `conveyor db-doctor --live`.
|
|
|
727
740
|
## Natural-Language Command Mapping
|
|
728
741
|
|
|
729
742
|
- "set up a Codex app Ralph loop": if the current session has Codex app thread
|
|
730
|
-
tools, call `create_thread` for
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
743
|
+
tools, call `create_thread` for fresh same-project manager and worker
|
|
744
|
+
threads unless the current thread is explicitly the manager, set readable
|
|
745
|
+
titles, run `conveyor create-disposable-binding` with both
|
|
746
|
+
`--manager-codex-app-thread-id`/`--manager-codex-app-thread-title` and
|
|
747
|
+
`--worker-codex-app-thread-id`/`--worker-codex-app-thread-title`, then send
|
|
748
|
+
the returned bootstrap prompts using `send_message_to_thread`. If those tools
|
|
749
|
+
are unavailable, run `create-disposable-binding` without thread metadata and
|
|
750
|
+
give the user the handoff prompts to paste into manually opened sessions. Keep
|
|
751
|
+
Dispatch as the source of durable communication, and make both Codex app
|
|
752
|
+
sessions repeat their role-specific `app-heartbeat` command while idle. Use
|
|
753
|
+
`app-loop-status` and `app-wakeup-plan` for operator status and stale-thread
|
|
754
|
+
recovery, and `app-wakeup-dispatch` when the manager needs an auditable
|
|
755
|
+
prepared/skipped/blocked wake receipt.
|
|
739
756
|
- "register this Codex session as the worker for dashboard setup <CODE>":
|
|
740
757
|
derive `dashboard-<CODE>-worker`, run `conveyor doctor-self`, then
|
|
741
758
|
`conveyor register-worker --name dashboard-<CODE>-worker --pid <PID> --cwd <CWD> --tmux-session <SESSION>`.
|