@ours.network/fleet 0.18.1 → 0.19.0-nightly.2
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 +57 -91
- package/dist/application/fleet-query-service.js +0 -12
- package/dist/application/role-creation-service.js +10 -7
- package/dist/application/types.d.ts +0 -11
- package/dist/briefing.js +6 -15
- package/dist/build-info.json +5 -5
- package/dist/cli.js +12 -37
- package/dist/config.d.ts +8 -6
- package/dist/config.js +46 -30
- package/dist/creation.d.ts +38 -22
- package/dist/creation.js +111 -24
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +44 -95
- package/dist/doctor.d.ts +5 -1
- package/dist/doctor.js +18 -11
- package/dist/fleet-proxy.d.ts +0 -5
- package/dist/harness/acp-agent.js +6 -11
- package/dist/harness/claude-code.js +11 -200
- package/dist/harness/codex.d.ts +1 -4
- package/dist/harness/codex.js +12 -70
- package/dist/harness/types.d.ts +4 -54
- package/dist/loops/manager.d.ts +1 -30
- package/dist/loops/manager.js +6 -69
- package/dist/loops/state.d.ts +0 -18
- package/dist/loops/state.js +0 -4
- package/dist/monitor.js +1 -1
- package/dist/ops.d.ts +3 -0
- package/dist/ops.js +8 -3
- package/dist/owner-channel/attachments.d.ts +25 -2
- package/dist/owner-channel/attachments.js +61 -5
- package/dist/owner-channel/channel.d.ts +29 -30
- package/dist/owner-channel/channel.js +291 -291
- package/dist/owner-channel/commands.js +89 -0
- package/dist/owner-channel/message-recovery.d.ts +25 -0
- package/dist/owner-channel/message-recovery.js +114 -0
- package/dist/owner-channel/notices.d.ts +0 -7
- package/dist/owner-channel/notices.js +0 -9
- package/dist/owner-channel/ours-client.d.ts +148 -0
- package/dist/owner-channel/ours-client.js +231 -0
- package/dist/rooms-tasks/cli.d.ts +4 -0
- package/dist/rooms-tasks/cli.js +565 -0
- package/dist/rooms-tasks/config.d.ts +15 -0
- package/dist/rooms-tasks/config.js +171 -0
- package/dist/rooms-tasks/cowork-adapter.d.ts +80 -0
- package/dist/rooms-tasks/cowork-adapter.js +48 -0
- package/dist/rooms-tasks/index.d.ts +7 -0
- package/dist/rooms-tasks/index.js +7 -0
- package/dist/rooms-tasks/room-state.d.ts +22 -0
- package/dist/rooms-tasks/room-state.js +117 -0
- package/dist/rooms-tasks/task-state.d.ts +31 -0
- package/dist/rooms-tasks/task-state.js +173 -0
- package/dist/rooms-tasks/templates.d.ts +6 -0
- package/dist/rooms-tasks/templates.js +80 -0
- package/dist/rooms-tasks/types.d.ts +153 -0
- package/dist/rooms-tasks/types.js +20 -0
- package/dist/runner.d.ts +0 -48
- package/dist/runner.js +94 -236
- package/dist/session/acp.d.ts +0 -104
- package/dist/session/acp.js +10 -213
- package/dist/session/conversation-normalizer.d.ts +0 -6
- package/dist/session/conversation-normalizer.js +10 -153
- package/dist/session/conversation-types.d.ts +4 -23
- package/dist/session/types.d.ts +0 -35
- package/dist/spawn.js +26 -33
- package/dist/supervisor/systemd.js +29 -2
- package/dist/watchdog/briefing.js +0 -7
- package/dist/watchdog/run.js +3 -3
- package/dist/web-app/assets/{TerminalView-C_G1ID2P.js → TerminalView-BAVk1Bot.js} +1 -1
- package/dist/web-app/assets/{index-BCBK78hw.js → index-C3S-xFRU.js} +5 -5
- package/dist/web-app/index.html +1 -1
- package/dist/worklog.d.ts +1 -7
- package/dist/worklog.js +39 -191
- package/package.json +3 -1
- package/dist/model-env.d.ts +0 -71
- package/dist/model-env.js +0 -106
- package/dist/owner-channel/mcp.d.ts +0 -24
- package/dist/owner-channel/mcp.js +0 -145
- package/dist/session/activity.d.ts +0 -31
- package/dist/session/activity.js +0 -48
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { createInterface } from 'node:readline';
|
|
4
|
-
export class OursMcpError extends Error {
|
|
5
|
-
}
|
|
6
|
-
/** Minimal MCP stdio client. One instance owns exactly one ours identity binding. */
|
|
7
|
-
export class OursMcpClient {
|
|
8
|
-
command;
|
|
9
|
-
env;
|
|
10
|
-
log;
|
|
11
|
-
child;
|
|
12
|
-
nextId = 0;
|
|
13
|
-
tail = Promise.resolve();
|
|
14
|
-
constructor(command = 'ours-mcp', env = {}, log = () => undefined) {
|
|
15
|
-
this.command = command;
|
|
16
|
-
this.env = env;
|
|
17
|
-
this.log = log;
|
|
18
|
-
}
|
|
19
|
-
async start() {
|
|
20
|
-
if (this.child && this.child.exitCode === null)
|
|
21
|
-
return;
|
|
22
|
-
// ours-mcp normally records the long-lived client PID so an identity lease
|
|
23
|
-
// survives connector churn. An owner-channel connector has the opposite
|
|
24
|
-
// lifecycle: each supervised attempt owns a fresh connector and must make
|
|
25
|
-
// its lease reclaimable when that connector exits, even though the fleet
|
|
26
|
-
// supervisor itself remains alive. POSIX exec preserves the shell PID as
|
|
27
|
-
// the proxy PID, giving the daemon an exact process-lifetime fence without
|
|
28
|
-
// interpolating the command path into shell text.
|
|
29
|
-
const child = spawn('/bin/sh', [
|
|
30
|
-
'-c', 'OURS_CLIENT_PID=$$; export OURS_CLIENT_PID; exec "$1" "$2"',
|
|
31
|
-
'ours-fleet-owner-proxy', this.command, 'proxy',
|
|
32
|
-
], {
|
|
33
|
-
env: {
|
|
34
|
-
...process.env,
|
|
35
|
-
...this.env,
|
|
36
|
-
// Bindings are keyed by this value. Sharing it would silently rebind a
|
|
37
|
-
// role's normal mailbox or another owner channel.
|
|
38
|
-
CLAUDE_CODE_SESSION_ID: `ours-fleet-owner-${process.pid}-${randomUUID()}`,
|
|
39
|
-
},
|
|
40
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
41
|
-
});
|
|
42
|
-
await new Promise((resolve, reject) => {
|
|
43
|
-
child.once('spawn', resolve);
|
|
44
|
-
child.once('error', reject);
|
|
45
|
-
});
|
|
46
|
-
this.child = child;
|
|
47
|
-
child.once('exit', (code, signal) => {
|
|
48
|
-
this.log(`ours-mcp proxy launcher exited (${code ?? signal ?? 'unknown'})`);
|
|
49
|
-
});
|
|
50
|
-
child.stdin.on('error', error => this.log(`ours-mcp stdin: ${error.message}`));
|
|
51
|
-
createInterface({ input: child.stderr }).on('line', line => this.log(`ours-mcp: ${line}`));
|
|
52
|
-
try {
|
|
53
|
-
await this.request('initialize', {
|
|
54
|
-
protocolVersion: '2025-03-26', capabilities: {},
|
|
55
|
-
clientInfo: { name: 'ours-fleet-owner-channel', version: '1' },
|
|
56
|
-
});
|
|
57
|
-
await this.notify('notifications/initialized', {});
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
await this.close();
|
|
61
|
-
throw error;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
async callTool(name, args = {}) {
|
|
65
|
-
const result = await this.request('tools/call', { name, arguments: args });
|
|
66
|
-
const text = (result.content ?? [])
|
|
67
|
-
.filter(item => item.type === 'text').map(item => item.text ?? '').join('\n').trim();
|
|
68
|
-
if (result.isError)
|
|
69
|
-
throw new OursMcpError(text || `ours tool ${name} failed`);
|
|
70
|
-
if (result.structuredContent !== undefined)
|
|
71
|
-
return result.structuredContent;
|
|
72
|
-
if (!text)
|
|
73
|
-
return {};
|
|
74
|
-
try {
|
|
75
|
-
return JSON.parse(text);
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
return text;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
async close() {
|
|
82
|
-
const child = this.child;
|
|
83
|
-
this.child = undefined;
|
|
84
|
-
if (!child || child.exitCode !== null)
|
|
85
|
-
return;
|
|
86
|
-
// EOF asks the proxy to close normally. Once this exact process exits, the
|
|
87
|
-
// daemon can reclaim its lease even while the supervisor stays alive.
|
|
88
|
-
child.stdin.end();
|
|
89
|
-
const exited = await new Promise(resolve => {
|
|
90
|
-
const timer = setTimeout(() => resolve(false), 1_000);
|
|
91
|
-
child.once('exit', () => { clearTimeout(timer); resolve(true); });
|
|
92
|
-
});
|
|
93
|
-
if (exited || child.exitCode !== null)
|
|
94
|
-
return;
|
|
95
|
-
child.kill('SIGTERM');
|
|
96
|
-
await new Promise(resolve => {
|
|
97
|
-
const timer = setTimeout(() => { child.kill('SIGKILL'); resolve(); }, 5_000);
|
|
98
|
-
child.once('exit', () => { clearTimeout(timer); resolve(); });
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
request(method, params) {
|
|
102
|
-
const run = this.tail.then(() => this.requestNow(method, params));
|
|
103
|
-
this.tail = run.then(() => undefined, () => undefined);
|
|
104
|
-
return run;
|
|
105
|
-
}
|
|
106
|
-
async requestNow(method, params) {
|
|
107
|
-
const child = this.child;
|
|
108
|
-
if (!child || child.exitCode !== null)
|
|
109
|
-
throw new OursMcpError('ours-mcp proxy is not running');
|
|
110
|
-
const id = ++this.nextId;
|
|
111
|
-
await this.write(child, { jsonrpc: '2.0', id, method, params });
|
|
112
|
-
const lines = createInterface({ input: child.stdout, crlfDelay: Infinity });
|
|
113
|
-
try {
|
|
114
|
-
for await (const line of lines) {
|
|
115
|
-
let response;
|
|
116
|
-
try {
|
|
117
|
-
response = JSON.parse(line);
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
continue;
|
|
121
|
-
}
|
|
122
|
-
if (response.id !== id)
|
|
123
|
-
continue;
|
|
124
|
-
if (response.error !== undefined)
|
|
125
|
-
throw new OursMcpError(JSON.stringify(response.error));
|
|
126
|
-
return response.result ?? {};
|
|
127
|
-
}
|
|
128
|
-
throw new OursMcpError('ours-mcp proxy closed its output');
|
|
129
|
-
}
|
|
130
|
-
finally {
|
|
131
|
-
lines.close();
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
async notify(method, params) {
|
|
135
|
-
const child = this.child;
|
|
136
|
-
if (!child || child.exitCode !== null)
|
|
137
|
-
throw new OursMcpError('ours-mcp proxy is not running');
|
|
138
|
-
await this.write(child, { jsonrpc: '2.0', method, params });
|
|
139
|
-
}
|
|
140
|
-
write(child, value) {
|
|
141
|
-
return new Promise((resolve, reject) => {
|
|
142
|
-
child.stdin.write(JSON.stringify(value) + '\n', error => error ? reject(error) : resolve());
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { SessionActivity } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* How long after the agent's last session update it still counts as working.
|
|
4
|
-
*
|
|
5
|
-
* FLEET-002: a wake delivered by ACP steering answers `startedNewTurn` and runs
|
|
6
|
-
* an entire turn that fleet never receives a `session/prompt` response for (ACP
|
|
7
|
-
* has no turn-end session update), so `readiness` stays `idle` throughout. Tool
|
|
8
|
-
* reservations and update recency are the only activity evidence fleet holds.
|
|
9
|
-
* The trade-off is deliberate and one-directional: at worst a role reads busy
|
|
10
|
-
* for one window after it genuinely stopped, instead of reading ready — or
|
|
11
|
-
* being classified stalled — while it is executing tools.
|
|
12
|
-
*/
|
|
13
|
-
export declare const ACTIVITY_WINDOW_MS = 60000;
|
|
14
|
-
export type ActivityState = 'active' | 'quiet' | 'unobservable';
|
|
15
|
-
export interface ObservedActivity {
|
|
16
|
-
state: ActivityState;
|
|
17
|
-
activeToolCalls?: number;
|
|
18
|
-
lastUpdateAt?: string;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Classify agent-side activity. `unobservable` is NOT `quiet`: a backend that
|
|
22
|
-
* cannot see the agent (tmux) has no evidence, and no evidence must never be
|
|
23
|
-
* reported as "doing nothing".
|
|
24
|
-
*/
|
|
25
|
-
export declare function classifyActivity(activity: SessionActivity | undefined, now?: number): ObservedActivity;
|
|
26
|
-
/**
|
|
27
|
-
* One operator-facing line that never lets turn occupancy pose as liveness:
|
|
28
|
-
* the readiness value is labelled as the turn field it is, and the activity
|
|
29
|
-
* verdict is stated separately with the evidence behind it.
|
|
30
|
-
*/
|
|
31
|
-
export declare function describeSessionState(readiness: string | undefined, activity: SessionActivity | undefined, now?: number): string;
|
package/dist/session/activity.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* How long after the agent's last session update it still counts as working.
|
|
3
|
-
*
|
|
4
|
-
* FLEET-002: a wake delivered by ACP steering answers `startedNewTurn` and runs
|
|
5
|
-
* an entire turn that fleet never receives a `session/prompt` response for (ACP
|
|
6
|
-
* has no turn-end session update), so `readiness` stays `idle` throughout. Tool
|
|
7
|
-
* reservations and update recency are the only activity evidence fleet holds.
|
|
8
|
-
* The trade-off is deliberate and one-directional: at worst a role reads busy
|
|
9
|
-
* for one window after it genuinely stopped, instead of reading ready — or
|
|
10
|
-
* being classified stalled — while it is executing tools.
|
|
11
|
-
*/
|
|
12
|
-
export const ACTIVITY_WINDOW_MS = 60_000;
|
|
13
|
-
/**
|
|
14
|
-
* Classify agent-side activity. `unobservable` is NOT `quiet`: a backend that
|
|
15
|
-
* cannot see the agent (tmux) has no evidence, and no evidence must never be
|
|
16
|
-
* reported as "doing nothing".
|
|
17
|
-
*/
|
|
18
|
-
export function classifyActivity(activity, now = Date.now()) {
|
|
19
|
-
if (!activity)
|
|
20
|
-
return { state: 'unobservable' };
|
|
21
|
-
const lastUpdate = activity.lastUpdateAt ? Date.parse(activity.lastUpdateAt) : NaN;
|
|
22
|
-
const recent = Number.isFinite(lastUpdate) && now - lastUpdate <= ACTIVITY_WINDOW_MS;
|
|
23
|
-
return {
|
|
24
|
-
state: activity.activeToolCalls > 0 || recent ? 'active' : 'quiet',
|
|
25
|
-
activeToolCalls: activity.activeToolCalls,
|
|
26
|
-
...(activity.lastUpdateAt ? { lastUpdateAt: activity.lastUpdateAt } : {}),
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* One operator-facing line that never lets turn occupancy pose as liveness:
|
|
31
|
-
* the readiness value is labelled as the turn field it is, and the activity
|
|
32
|
-
* verdict is stated separately with the evidence behind it.
|
|
33
|
-
*/
|
|
34
|
-
export function describeSessionState(readiness, activity, now = Date.now()) {
|
|
35
|
-
const observed = classifyActivity(activity, now);
|
|
36
|
-
const evidence = [];
|
|
37
|
-
if (observed.activeToolCalls)
|
|
38
|
-
evidence.push(`${observed.activeToolCalls} tool calls in flight`);
|
|
39
|
-
if (observed.lastUpdateAt) {
|
|
40
|
-
const age = Math.max(0, Math.round((now - Date.parse(observed.lastUpdateAt)) / 1000));
|
|
41
|
-
if (Number.isFinite(age))
|
|
42
|
-
evidence.push(`last agent update ${age}s ago`);
|
|
43
|
-
}
|
|
44
|
-
const detail = observed.state === 'unobservable'
|
|
45
|
-
? 'no agent-side evidence on this backend'
|
|
46
|
-
: evidence.join(', ') || 'no updates yet';
|
|
47
|
-
return `turn: ${readiness ?? 'unknown'} (turn occupancy only) | activity: ${observed.state} (${detail})`;
|
|
48
|
-
}
|