@zgeoff/atc 2.6.0 → 2.8.0
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 +4 -0
- package/package.json +3 -1
- package/src/agents/agent-adapter.ts +11 -0
- package/src/agents/atc-bridge-files.ts +11 -0
- package/src/agents/build-cli-argv.ts +13 -0
- package/src/agents/build-cli-command.ts +4 -9
- package/src/agents/claude-adapter.ts +23 -2
- package/src/agents/codex-adapter.ts +2 -0
- package/src/agents/gateway-adapter.ts +23 -1
- package/src/agents/grok-adapter.ts +2 -0
- package/src/agents/write-atc-bridge.ts +42 -0
- package/src/agents/write-hook-settings.ts +2 -1
- package/src/cli.ts +51 -0
- package/src/client/daemon-client.ts +13 -1
- package/src/daemon/answer-byte-cap.ts +1 -0
- package/src/daemon/build-headless-env.ts +23 -0
- package/src/daemon/build-session-message-event.ts +29 -0
- package/src/daemon/build-session-report-event.ts +24 -0
- package/src/daemon/daemon-connection.ts +200 -0
- package/src/daemon/daemon.ts +264 -3
- package/src/daemon/hooks.ts +13 -4
- package/src/daemon/mint-message-id.ts +12 -0
- package/src/daemon/parse-report.ts +44 -0
- package/src/daemon/session-runtime.ts +9 -0
- package/src/daemon/start-headless-run.ts +10 -2
- package/src/daemon/start-headless-turn.ts +1 -0
- package/src/daemon/tap-registry.ts +67 -0
- package/src/mcp-server.ts +56 -0
- package/src/protocol/request-param-schemas.ts +12 -0
- package/src/report.ts +59 -0
- package/src/shared/message-id.ts +7 -0
- package/src/shared/report-kinds.ts +1 -0
- package/src/shared/report.ts +18 -2
- package/src/shared/to-message-id.ts +11 -0
- package/src/store/message-owner.ts +9 -0
- package/src/store/message-record.ts +18 -0
- package/src/store/run-migrations.ts +52 -2
- package/src/store/state-store.ts +144 -2
- package/src/tap.ts +120 -0
package/src/tap.ts
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { DaemonClient } from './client/daemon-client';
|
|
3
|
+
import { DaemonError } from './protocol/daemon-error';
|
|
4
|
+
import { daemonSocketPath } from './shared/config';
|
|
5
|
+
import { getBuild } from './shared/get-build';
|
|
6
|
+
|
|
7
|
+
const INBOX_MESSAGE_SCHEMA = z.looseObject({
|
|
8
|
+
message: z.string(),
|
|
9
|
+
from: z.string(),
|
|
10
|
+
text: z.string(),
|
|
11
|
+
sentAt: z.number(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
interface InboxMessage {
|
|
15
|
+
readonly message: string;
|
|
16
|
+
readonly from: string;
|
|
17
|
+
readonly text: string;
|
|
18
|
+
readonly sentAt: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Streams one session's inbox to stdout, one NDJSON line per message, and
|
|
23
|
+
* acks each message once its line is written. It talks to the daemon that
|
|
24
|
+
* is already running and never boots one: a tap runs inside the session it
|
|
25
|
+
* serves, so a restart from here would take the whole fleet down. Exits 1
|
|
26
|
+
* with a hint when no daemon listens or the session cannot be tapped, and
|
|
27
|
+
* 0 once the daemon closes the connection or ends the subscription because
|
|
28
|
+
* another tap replaced it or the session was removed.
|
|
29
|
+
*/
|
|
30
|
+
export async function runTap(session: string): Promise<void> {
|
|
31
|
+
let client: DaemonClient;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
client = await DaemonClient.open(daemonSocketPath);
|
|
35
|
+
} catch {
|
|
36
|
+
console.error(`atc tap: no daemon at ${daemonSocketPath} — start atc first`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const closed = Promise.withResolvers<void>();
|
|
41
|
+
let written: Promise<void> = Promise.resolve();
|
|
42
|
+
|
|
43
|
+
client.onClose = () => {
|
|
44
|
+
closed.resolve();
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
client.onEvent = (event) => {
|
|
48
|
+
if (event.ev === 'InboxClosed' && event['s'] === session) {
|
|
49
|
+
closed.resolve();
|
|
50
|
+
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (event.ev !== 'InboxMessage' || event['s'] !== session) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const parsed = INBOX_MESSAGE_SCHEMA.safeParse(event);
|
|
59
|
+
|
|
60
|
+
if (!parsed.success) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const msg: InboxMessage = {
|
|
65
|
+
message: parsed.data.message,
|
|
66
|
+
from: parsed.data.from,
|
|
67
|
+
text: parsed.data.text,
|
|
68
|
+
sentAt: parsed.data.sentAt,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const previous = written;
|
|
72
|
+
|
|
73
|
+
written = (async () => {
|
|
74
|
+
await previous;
|
|
75
|
+
await ackInboxMessage(client, session, msg);
|
|
76
|
+
})();
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
await client.sendHello(getBuild());
|
|
81
|
+
await client.sendRequest('session.tap', { session });
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(`atc tap: ${formatError(error)}`);
|
|
84
|
+
client.stop();
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
await closed.promise;
|
|
89
|
+
await written;
|
|
90
|
+
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface RequestSender {
|
|
95
|
+
readonly sendRequest: DaemonClient['sendRequest'];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function ackInboxMessage(
|
|
99
|
+
client: RequestSender,
|
|
100
|
+
session: string,
|
|
101
|
+
msg: InboxMessage,
|
|
102
|
+
): Promise<void> {
|
|
103
|
+
const line = `${JSON.stringify({ id: msg.message, from: msg.from, text: msg.text, sentAt: msg.sentAt })}\n`;
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
await Bun.write(Bun.stdout, line);
|
|
107
|
+
} catch {
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
await client.sendRequest('message.ack', { session, message: msg.message });
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error(`atc tap: ${formatError(error)}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function formatError(error: unknown): string {
|
|
119
|
+
return error instanceof DaemonError ? `${error.code}: ${error.message}` : String(error);
|
|
120
|
+
}
|