alink-cli 0.9.1 → 0.10.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 +19 -20
- package/dist/bin.mjs +284 -48
- package/dist/bin.mjs.map +1 -1
- package/dist/daemon.js +1 -1
- package/dist/ui.js +2 -1
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -1,50 +1,49 @@
|
|
|
1
1
|
# alink-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
在手机或浏览器上,继续使用运行在你自己电脑上的 Claude Code、Codex、Gemini、Qwen、Kimi 等编码 agent。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> **Local-first:** agent 在你的电脑上运行,项目文件和任务记录保存在本机。AgentLink 只负责加密连接设备,Hub 不保存你的对话。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## 三步开始
|
|
8
|
+
|
|
9
|
+
需要 Node.js 22.16 或更高版本。
|
|
10
|
+
|
|
11
|
+
1. 安装 CLI
|
|
8
12
|
|
|
9
13
|
```bash
|
|
10
14
|
npm install -g alink-cli
|
|
11
15
|
```
|
|
12
16
|
|
|
13
|
-
2.
|
|
17
|
+
2. 启动 AgentLink,并按提示登录
|
|
14
18
|
|
|
15
19
|
```bash
|
|
16
20
|
alink-cli
|
|
17
21
|
```
|
|
18
22
|
|
|
19
|
-
3.
|
|
23
|
+
3. 在手机或浏览器打开 [AgentLink 控制台](https://link.harmopath.com/login)
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
登录同一个账号,你的电脑会自动出现。选择 agent 和工作目录,就可以开始任务或继续已有工作。
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
关闭浏览器或锁屏后,任务仍会在你的电脑上继续运行。
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
## 为什么是 local-first
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
- **本地运行:** agent 和命令都在你的电脑上执行。
|
|
32
|
+
- **本地保存:** AgentLink 不会把你的项目复制到云端,任务记录保存在本机。
|
|
33
|
+
- **安全连接:** 设备之间端到端加密;Hub 只转发连接,不保存对话内容。
|
|
34
|
+
- **账号隔离:** 只有登录你账号的设备才能看到你的电脑。
|
|
28
35
|
|
|
29
36
|
## 常用命令
|
|
30
37
|
|
|
31
38
|
```bash
|
|
32
39
|
alink-cli # 启动或管理连接
|
|
33
|
-
alink-cli login #
|
|
40
|
+
alink-cli login # 登录并添加这台电脑
|
|
34
41
|
alink-cli status # 查看连接状态
|
|
35
42
|
alink-cli machines # 查看账号下的电脑
|
|
36
43
|
alink-cli logout # 退出登录
|
|
37
44
|
```
|
|
38
45
|
|
|
39
|
-
## 常用选项
|
|
40
|
-
|
|
41
|
-
- `--dir <path>`:设置默认工作目录。
|
|
42
|
-
- `--hub <url>`:连接到自己的 hub(自托管时使用)。
|
|
43
|
-
- `--no-tui`:跳过交互界面,直接前台运行(适合脚本或非交互环境)。
|
|
44
|
-
|
|
45
|
-
需要 Node.js 22.16 或更高版本。
|
|
46
|
-
|
|
47
46
|
## 了解更多
|
|
48
47
|
|
|
49
|
-
-
|
|
50
|
-
-
|
|
48
|
+
- [完整文档](https://github.com/baichen99/agentlink)
|
|
49
|
+
- [提交问题](https://github.com/baichen99/agentlink/issues)
|
package/dist/bin.mjs
CHANGED
|
@@ -14414,6 +14414,15 @@ Union([DispatchableClientOrchestrationCommand, Union([
|
|
|
14414
14414
|
session: OrchestrationSession,
|
|
14415
14415
|
createdAt: IsoDateTime
|
|
14416
14416
|
}),
|
|
14417
|
+
Struct({
|
|
14418
|
+
type: Literal("thread.message.user.import"),
|
|
14419
|
+
commandId: CommandId,
|
|
14420
|
+
threadId: ThreadId,
|
|
14421
|
+
messageId: MessageId,
|
|
14422
|
+
turnId: TurnId,
|
|
14423
|
+
text: String$1,
|
|
14424
|
+
createdAt: IsoDateTime
|
|
14425
|
+
}),
|
|
14417
14426
|
Struct({
|
|
14418
14427
|
type: Literal("thread.message.assistant.delta"),
|
|
14419
14428
|
commandId: CommandId,
|
|
@@ -35789,6 +35798,32 @@ const decideOrchestrationCommand = fn("decideOrchestrationCommand")(function* ({
|
|
|
35789
35798
|
updatedAt: command.createdAt
|
|
35790
35799
|
}
|
|
35791
35800
|
};
|
|
35801
|
+
case "thread.message.user.import":
|
|
35802
|
+
yield* requireThread({
|
|
35803
|
+
readModel,
|
|
35804
|
+
command,
|
|
35805
|
+
threadId: command.threadId
|
|
35806
|
+
});
|
|
35807
|
+
return {
|
|
35808
|
+
...yield* withEventBase({
|
|
35809
|
+
aggregateKind: "thread",
|
|
35810
|
+
aggregateId: command.threadId,
|
|
35811
|
+
occurredAt: command.createdAt,
|
|
35812
|
+
commandId: command.commandId
|
|
35813
|
+
}),
|
|
35814
|
+
type: "thread.message-sent",
|
|
35815
|
+
payload: {
|
|
35816
|
+
threadId: command.threadId,
|
|
35817
|
+
messageId: command.messageId,
|
|
35818
|
+
role: "user",
|
|
35819
|
+
text: command.text,
|
|
35820
|
+
attachments: [],
|
|
35821
|
+
turnId: command.turnId,
|
|
35822
|
+
streaming: false,
|
|
35823
|
+
createdAt: command.createdAt,
|
|
35824
|
+
updatedAt: command.createdAt
|
|
35825
|
+
}
|
|
35826
|
+
};
|
|
35792
35827
|
case "thread.message.assistant.complete":
|
|
35793
35828
|
yield* requireThread({
|
|
35794
35829
|
readModel,
|
|
@@ -50911,6 +50946,29 @@ const normalizeDispatchCommand = (command) => gen(function* () {
|
|
|
50911
50946
|
};
|
|
50912
50947
|
});
|
|
50913
50948
|
//#endregion
|
|
50949
|
+
//#region src/orchestration/Services/ProviderRuntimeIngestion.ts
|
|
50950
|
+
/**
|
|
50951
|
+
* ProviderRuntimeIngestionService - Provider runtime ingestion service interface.
|
|
50952
|
+
*
|
|
50953
|
+
* Owns background workers that consume provider runtime streams and emit
|
|
50954
|
+
* orchestration commands/events.
|
|
50955
|
+
*
|
|
50956
|
+
* @module ProviderRuntimeIngestionService
|
|
50957
|
+
*/
|
|
50958
|
+
var ProviderRuntimeReconciliationError = class extends TaggedErrorClass()("ProviderRuntimeReconciliationError", {
|
|
50959
|
+
threadId: ThreadId,
|
|
50960
|
+
detail: String$1,
|
|
50961
|
+
cause: optional$3(Defect())
|
|
50962
|
+
}) {
|
|
50963
|
+
get message() {
|
|
50964
|
+
return `Failed to reconcile provider thread '${this.threadId}': ${this.detail}`;
|
|
50965
|
+
}
|
|
50966
|
+
};
|
|
50967
|
+
/**
|
|
50968
|
+
* ProviderRuntimeIngestionService - Service tag for runtime ingestion workers.
|
|
50969
|
+
*/
|
|
50970
|
+
var ProviderRuntimeIngestionService = class extends Service$2()("t3/orchestration/Services/ProviderRuntimeIngestion/ProviderRuntimeIngestionService") {};
|
|
50971
|
+
//#endregion
|
|
50914
50972
|
//#region src/observability/RpcInstrumentation.ts
|
|
50915
50973
|
const RPC_SPAN_PREFIX = "ws.rpc";
|
|
50916
50974
|
const DEFAULT_RPC_SPAN_ATTRIBUTES = {
|
|
@@ -52518,6 +52576,7 @@ const wsRpcServices = gen(function* () {
|
|
|
52518
52576
|
crypto: yield* Crypto,
|
|
52519
52577
|
projectionSnapshotQuery: yield* ProjectionSnapshotQuery,
|
|
52520
52578
|
orchestrationEngine: yield* OrchestrationEngineService,
|
|
52579
|
+
providerRuntimeIngestion: yield* ProviderRuntimeIngestionService,
|
|
52521
52580
|
keybindings: yield* Keybindings,
|
|
52522
52581
|
externalLauncher: yield* ExternalLauncher,
|
|
52523
52582
|
providerRegistry: yield* ProviderRegistry,
|
|
@@ -52538,7 +52597,7 @@ const wsRpcServices = gen(function* () {
|
|
|
52538
52597
|
});
|
|
52539
52598
|
const makeWsRpcLayer = (currentSession) => WsRpcGroup.toLayer(gen(function* () {
|
|
52540
52599
|
const currentSessionId = currentSession.sessionId;
|
|
52541
|
-
const { crypto, projectionSnapshotQuery, orchestrationEngine, keybindings, externalLauncher, providerRegistry, config, lifecycleEvents, serverSettings, startup, workspaceEntries, workspaceFileSystem, gitWorkspace, terminalManager, serverEnvironment, backgroundPolicy, serverAuth, bootstrapCredentials, sessions } = yield* wsRpcServices;
|
|
52600
|
+
const { crypto, projectionSnapshotQuery, orchestrationEngine, providerRuntimeIngestion, keybindings, externalLauncher, providerRegistry, config, lifecycleEvents, serverSettings, startup, workspaceEntries, workspaceFileSystem, gitWorkspace, terminalManager, serverEnvironment, backgroundPolicy, serverAuth, bootstrapCredentials, sessions } = yield* wsRpcServices;
|
|
52542
52601
|
const providerMaintenanceRunner = yield* ProviderMaintenanceRunner;
|
|
52543
52602
|
const rpcClientIds = yield* make$63(/* @__PURE__ */ new Set());
|
|
52544
52603
|
yield* addFinalizer(() => get$3(rpcClientIds).pipe(flatMap$1((clientIds) => forEach(clientIds, (clientId) => backgroundPolicy.removeRpcClient(currentSessionId, clientId), { discard: true })), ignore$1));
|
|
@@ -52681,7 +52740,8 @@ const makeWsRpcLayer = (currentSession) => WsRpcGroup.toLayer(gen(function* () {
|
|
|
52681
52740
|
});
|
|
52682
52741
|
const dispatchNormalizedCommand = (normalizedCommand) => {
|
|
52683
52742
|
const dispatchEffect = normalizedCommand.type === "thread.turn.start" && normalizedCommand.bootstrap ? dispatchBootstrapTurnStart(normalizedCommand) : orchestrationEngine.dispatch(normalizedCommand).pipe(mapError((cause) => toDispatchCommandError(cause, "Failed to dispatch orchestration command")));
|
|
52684
|
-
|
|
52743
|
+
const guardedDispatchEffect = normalizedCommand.type === "thread.turn.start" && normalizedCommand.bootstrap === void 0 ? providerRuntimeIngestion.reconcileThread(normalizedCommand.threadId, { requireWritable: true }).pipe(mapError((cause) => toDispatchCommandError(cause, "Failed to reconcile provider thread")), andThen(dispatchEffect)) : dispatchEffect;
|
|
52744
|
+
return startup.enqueueCommand(guardedDispatchEffect).pipe(mapError((cause) => toDispatchCommandError(cause, "Failed to dispatch orchestration command")));
|
|
52685
52745
|
};
|
|
52686
52746
|
const loadServerConfig = gen(function* () {
|
|
52687
52747
|
const keybindingsConfig = yield* keybindings.loadConfigState;
|
|
@@ -52782,6 +52842,10 @@ const makeWsRpcLayer = (currentSession) => WsRpcGroup.toLayer(gen(function* () {
|
|
|
52782
52842
|
const liveBuffer = yield* unbounded$2();
|
|
52783
52843
|
yield* forkScoped(liveStream.pipe(runForEach((item) => offer$1(liveBuffer, item))));
|
|
52784
52844
|
const bufferedLiveStream = fromQueue(liveBuffer);
|
|
52845
|
+
yield* providerRuntimeIngestion.reconcileThread(input.threadId).pipe(mapError((cause) => new OrchestrationGetSnapshotError({
|
|
52846
|
+
message: cause.message,
|
|
52847
|
+
cause
|
|
52848
|
+
})));
|
|
52785
52849
|
if (input.afterSequence !== void 0) {
|
|
52786
52850
|
const afterSequence = input.afterSequence;
|
|
52787
52851
|
const replayGap = (yield* orchestrationEngine.latestSequence) - afterSequence;
|
|
@@ -54914,6 +54978,29 @@ const ProviderServiceLive = effect(ProviderService, fn("makeProviderService")(fu
|
|
|
54914
54978
|
});
|
|
54915
54979
|
const getCapabilities = (instanceId) => registry.getByInstance(instanceId).pipe(map$4((adapter) => adapter.capabilities));
|
|
54916
54980
|
const getInstanceInfo = (instanceId) => registry.getInstanceInfo(instanceId);
|
|
54981
|
+
const readThread = fn("readThread")(function* (threadId) {
|
|
54982
|
+
const binding = getOrUndefined(yield* directory.getBinding(threadId));
|
|
54983
|
+
if (!binding) return yield* toValidationError("ProviderService.readThread", `Cannot read thread '${threadId}' because no persisted provider binding exists.`);
|
|
54984
|
+
const instanceId = yield* requireBindingInstanceId("ProviderService.readThread", binding);
|
|
54985
|
+
const adapter = yield* registry.getByInstance(instanceId);
|
|
54986
|
+
const cwd = readPersistedCwd(binding.runtimePayload);
|
|
54987
|
+
const modelSelection = readPersistedModelSelection(binding.runtimePayload);
|
|
54988
|
+
const hasResumeCursor = binding.resumeCursor !== null && binding.resumeCursor !== void 0;
|
|
54989
|
+
return yield* adapter.readThread({
|
|
54990
|
+
threadId,
|
|
54991
|
+
provider: binding.provider,
|
|
54992
|
+
providerInstanceId: instanceId,
|
|
54993
|
+
...cwd ? { cwd } : {},
|
|
54994
|
+
...modelSelection ? { modelSelection } : {},
|
|
54995
|
+
...hasResumeCursor ? { resumeCursor: binding.resumeCursor } : {},
|
|
54996
|
+
runtimeMode: binding.runtimeMode ?? "full-access"
|
|
54997
|
+
});
|
|
54998
|
+
});
|
|
54999
|
+
const ensureSession = (threadId) => resolveRoutableSession({
|
|
55000
|
+
threadId,
|
|
55001
|
+
operation: "ProviderService.ensureSession",
|
|
55002
|
+
allowRecovery: true
|
|
55003
|
+
}).pipe(asVoid);
|
|
54917
55004
|
const rollbackConversation = fn("rollbackConversation")(function* (rawInput) {
|
|
54918
55005
|
const input = yield* decodeInputOrValidationError({
|
|
54919
55006
|
operation: "ProviderService.rollbackConversation",
|
|
@@ -54984,6 +55071,8 @@ const ProviderServiceLive = effect(ProviderService, fn("makeProviderService")(fu
|
|
|
54984
55071
|
listSessions,
|
|
54985
55072
|
getCapabilities,
|
|
54986
55073
|
getInstanceInfo,
|
|
55074
|
+
readThread,
|
|
55075
|
+
ensureSession,
|
|
54987
55076
|
rollbackConversation,
|
|
54988
55077
|
get streamEvents() {
|
|
54989
55078
|
return fromPubSub(runtimeEventPubSub);
|
|
@@ -59352,8 +59441,8 @@ const makeClaudeAdapter = fn("makeClaudeAdapter")(function* (claudeSettings, opt
|
|
|
59352
59441
|
catch: (cause) => toRequestError(threadId, "turn/interrupt", cause)
|
|
59353
59442
|
});
|
|
59354
59443
|
});
|
|
59355
|
-
const readThread = fn("readThread")(function* (
|
|
59356
|
-
const context = yield* requireSession(threadId);
|
|
59444
|
+
const readThread = fn("readThread")(function* (input) {
|
|
59445
|
+
const context = yield* requireSession(input.threadId);
|
|
59357
59446
|
return yield* snapshotThread(context);
|
|
59358
59447
|
});
|
|
59359
59448
|
const rollbackThread = fn("rollbackThread")(function* (threadId, numTurns) {
|
|
@@ -78832,8 +78921,14 @@ function updateSession(sessionRef, updates) {
|
|
|
78832
78921
|
function parseThreadSnapshot(response) {
|
|
78833
78922
|
return {
|
|
78834
78923
|
threadId: response.thread.id,
|
|
78924
|
+
createdAt: response.thread.createdAt,
|
|
78925
|
+
updatedAt: response.thread.updatedAt,
|
|
78926
|
+
status: response.thread.status,
|
|
78835
78927
|
turns: response.thread.turns.map((turn) => ({
|
|
78836
78928
|
id: TurnId.make(turn.id),
|
|
78929
|
+
status: turn.status,
|
|
78930
|
+
...turn.startedAt !== void 0 && turn.startedAt !== null ? { startedAt: turn.startedAt } : {},
|
|
78931
|
+
...turn.completedAt !== void 0 && turn.completedAt !== null ? { completedAt: turn.completedAt } : {},
|
|
78837
78932
|
items: turn.items
|
|
78838
78933
|
}))
|
|
78839
78934
|
};
|
|
@@ -78874,6 +78969,10 @@ const makeCodexSessionRuntime = (options) => gen(function* () {
|
|
|
78874
78969
|
})));
|
|
78875
78970
|
const clientContext = yield* layerChildProcess$1(child).pipe(build, provideService(Scope, runtimeScope));
|
|
78876
78971
|
const client = yield* service(CodexAppServerClient).pipe(provide$1(clientContext));
|
|
78972
|
+
const initializeClient = yield* cached(gen(function* () {
|
|
78973
|
+
yield* client.request("initialize", buildCodexInitializeParams());
|
|
78974
|
+
yield* client.notify("initialized", void 0);
|
|
78975
|
+
}));
|
|
78877
78976
|
const serverNotifications = yield* unbounded$2();
|
|
78878
78977
|
const nowIso = map$4(now$1, formatIso);
|
|
78879
78978
|
const randomUUIDv4 = (purpose) => crypto.randomUUIDv4.pipe(mapError((cause) => new CodexAppServerIdentifierGenerationError({
|
|
@@ -79352,8 +79451,7 @@ const makeCodexSessionRuntime = (options) => gen(function* () {
|
|
|
79352
79451
|
}))), forkIn(runtimeScope));
|
|
79353
79452
|
const start = fn("CodexSessionRuntime.start")(function* () {
|
|
79354
79453
|
yield* emitSessionEvent("session/connecting", "Starting Codex App Server session.");
|
|
79355
|
-
yield*
|
|
79356
|
-
yield* client.notify("initialized", void 0);
|
|
79454
|
+
yield* initializeClient;
|
|
79357
79455
|
const requestedModel = normalizeCodexModelSlug(options.model);
|
|
79358
79456
|
const opened = yield* openCodexThread({
|
|
79359
79457
|
client,
|
|
@@ -79447,6 +79545,7 @@ const makeCodexSessionRuntime = (options) => gen(function* () {
|
|
|
79447
79545
|
}),
|
|
79448
79546
|
readThread: gen(function* () {
|
|
79449
79547
|
const providerThreadId = yield* readProviderThreadId;
|
|
79548
|
+
yield* initializeClient;
|
|
79450
79549
|
return parseThreadSnapshot(yield* client.request("thread/read", {
|
|
79451
79550
|
threadId: providerThreadId,
|
|
79452
79551
|
includeTurns: true
|
|
@@ -79748,6 +79847,7 @@ function runtimeEventBase(event, canonicalThreadId) {
|
|
|
79748
79847
|
return {
|
|
79749
79848
|
eventId: event.id,
|
|
79750
79849
|
provider: event.provider,
|
|
79850
|
+
...event.providerInstanceId !== void 0 ? { providerInstanceId: event.providerInstanceId } : {},
|
|
79751
79851
|
threadId: canonicalThreadId,
|
|
79752
79852
|
createdAt: event.createdAt,
|
|
79753
79853
|
...event.turnId ? { turnId: event.turnId } : {},
|
|
@@ -80473,6 +80573,90 @@ function mapToRuntimeEvents(event, canonicalThreadId) {
|
|
|
80473
80573
|
}
|
|
80474
80574
|
return [];
|
|
80475
80575
|
}
|
|
80576
|
+
function unixSecondsToIso(value, fallback) {
|
|
80577
|
+
return (/* @__PURE__ */ new Date((value ?? fallback) * 1e3)).toISOString();
|
|
80578
|
+
}
|
|
80579
|
+
function summarizeCodexUserInput(input) {
|
|
80580
|
+
switch (input.type) {
|
|
80581
|
+
case "text": return input.text;
|
|
80582
|
+
case "image":
|
|
80583
|
+
case "localImage": return "[Image input omitted]";
|
|
80584
|
+
case "audio":
|
|
80585
|
+
case "localAudio": return "[Audio input omitted]";
|
|
80586
|
+
case "skill": return `[Skill: ${input.name}]`;
|
|
80587
|
+
case "mention": return `[Mention: ${input.name}]`;
|
|
80588
|
+
}
|
|
80589
|
+
}
|
|
80590
|
+
/** Converts a native `thread/read` snapshot through the same item mapper as live events. */
|
|
80591
|
+
function mapCodexThreadSnapshot(canonicalThreadId, providerInstanceId, snapshot) {
|
|
80592
|
+
const makeEvent = (input) => {
|
|
80593
|
+
const itemId = input.item ? ProviderItemId.make(input.item.id) : void 0;
|
|
80594
|
+
const suffix = itemId ? `item:${itemId}:completed` : input.method.replace("/", ":");
|
|
80595
|
+
const createdAt = unixSecondsToIso(input.method === "turn/started" ? input.turn.startedAt : input.turn.completedAt, input.method === "turn/started" ? snapshot.createdAt : snapshot.updatedAt);
|
|
80596
|
+
const payload = input.method === "item/completed" && input.item ? {
|
|
80597
|
+
completedAtMs: Date.parse(createdAt),
|
|
80598
|
+
threadId: snapshot.threadId,
|
|
80599
|
+
turnId: input.turn.id,
|
|
80600
|
+
item: input.item
|
|
80601
|
+
} : {
|
|
80602
|
+
threadId: snapshot.threadId,
|
|
80603
|
+
turn: input.turn
|
|
80604
|
+
};
|
|
80605
|
+
return {
|
|
80606
|
+
id: EventId.make(`codex-snapshot:${snapshot.threadId}:turn:${input.turn.id}:${suffix}`),
|
|
80607
|
+
kind: "notification",
|
|
80608
|
+
provider: PROVIDER,
|
|
80609
|
+
providerInstanceId,
|
|
80610
|
+
threadId: canonicalThreadId,
|
|
80611
|
+
createdAt,
|
|
80612
|
+
method: input.method,
|
|
80613
|
+
turnId: input.turn.id,
|
|
80614
|
+
...itemId ? { itemId } : {},
|
|
80615
|
+
payload
|
|
80616
|
+
};
|
|
80617
|
+
};
|
|
80618
|
+
return {
|
|
80619
|
+
threadId: canonicalThreadId,
|
|
80620
|
+
providerThreadId: snapshot.threadId,
|
|
80621
|
+
status: snapshot.status.type,
|
|
80622
|
+
activeFlags: snapshot.status.type === "active" ? snapshot.status.activeFlags : [],
|
|
80623
|
+
turns: snapshot.turns.map((turn) => {
|
|
80624
|
+
const terminal = turn.status !== "inProgress";
|
|
80625
|
+
const nativeEvents = [
|
|
80626
|
+
makeEvent({
|
|
80627
|
+
method: "turn/started",
|
|
80628
|
+
turn
|
|
80629
|
+
}),
|
|
80630
|
+
...terminal ? turn.items.map((item) => makeEvent({
|
|
80631
|
+
method: "item/completed",
|
|
80632
|
+
turn,
|
|
80633
|
+
item
|
|
80634
|
+
})) : [],
|
|
80635
|
+
...terminal ? [makeEvent({
|
|
80636
|
+
method: "turn/completed",
|
|
80637
|
+
turn
|
|
80638
|
+
})] : []
|
|
80639
|
+
];
|
|
80640
|
+
const userMessages = turn.items.flatMap((item) => {
|
|
80641
|
+
if (item.type !== "userMessage") return [];
|
|
80642
|
+
const text = item.content.map(summarizeCodexUserInput).filter((part) => part.trim().length > 0).join("\n");
|
|
80643
|
+
return [{
|
|
80644
|
+
id: ProviderItemId.make(item.id),
|
|
80645
|
+
text: text.length > 0 ? text : "[Empty user message]"
|
|
80646
|
+
}];
|
|
80647
|
+
});
|
|
80648
|
+
return {
|
|
80649
|
+
id: turn.id,
|
|
80650
|
+
status: turn.status,
|
|
80651
|
+
...turn.startedAt !== void 0 ? { startedAt: unixSecondsToIso(turn.startedAt, snapshot.createdAt) } : {},
|
|
80652
|
+
...turn.completedAt !== void 0 ? { completedAt: unixSecondsToIso(turn.completedAt, snapshot.updatedAt) } : {},
|
|
80653
|
+
items: turn.items,
|
|
80654
|
+
userMessages,
|
|
80655
|
+
runtimeEvents: nativeEvents.flatMap((event) => mapToRuntimeEvents(event, canonicalThreadId))
|
|
80656
|
+
};
|
|
80657
|
+
})
|
|
80658
|
+
};
|
|
80659
|
+
}
|
|
80476
80660
|
/**
|
|
80477
80661
|
* Build a Codex provider adapter bound to a specific `CodexSettings` payload.
|
|
80478
80662
|
*
|
|
@@ -80492,16 +80676,10 @@ const makeCodexAdapter = fn("makeCodexAdapter")(function* (codexConfig, options)
|
|
|
80492
80676
|
const managedNativeEventLogger = options?.nativeEventLogger === void 0 ? nativeEventLogger : void 0;
|
|
80493
80677
|
const runtimeEventQueue = yield* unbounded$2();
|
|
80494
80678
|
const sessions = /* @__PURE__ */ new Map();
|
|
80495
|
-
const
|
|
80496
|
-
|
|
80497
|
-
provider: PROVIDER,
|
|
80498
|
-
operation: "startSession",
|
|
80499
|
-
issue: `Expected provider '${PROVIDER}' but received '${input.provider}'.`
|
|
80500
|
-
});
|
|
80501
|
-
const existing = sessions.get(input.threadId);
|
|
80502
|
-
if (existing && !existing.stopped) yield* suspend(() => stopSessionInternal(existing));
|
|
80679
|
+
const createRuntime = options?.makeRuntime ?? makeCodexSessionRuntime;
|
|
80680
|
+
const toRuntimeInput = (input) => {
|
|
80503
80681
|
const serviceTier = input.modelSelection?.instanceId === boundInstanceId ? getCodexServiceTierOptionValue(input.modelSelection) : void 0;
|
|
80504
|
-
|
|
80682
|
+
return {
|
|
80505
80683
|
threadId: input.threadId,
|
|
80506
80684
|
providerInstanceId: boundInstanceId,
|
|
80507
80685
|
cwd: input.cwd ?? process.cwd(),
|
|
@@ -80514,10 +80692,20 @@ const makeCodexAdapter = fn("makeCodexAdapter")(function* (codexConfig, options)
|
|
|
80514
80692
|
...input.modelSelection?.instanceId === boundInstanceId ? { model: input.modelSelection.model } : {},
|
|
80515
80693
|
...serviceTier ? { serviceTier } : {}
|
|
80516
80694
|
};
|
|
80695
|
+
};
|
|
80696
|
+
const startSession = (input) => scoped(gen(function* () {
|
|
80697
|
+
if (input.provider !== void 0 && input.provider !== PROVIDER) return yield* new ProviderAdapterValidationError({
|
|
80698
|
+
provider: PROVIDER,
|
|
80699
|
+
operation: "startSession",
|
|
80700
|
+
issue: `Expected provider '${PROVIDER}' but received '${input.provider}'.`
|
|
80701
|
+
});
|
|
80702
|
+
const existing = sessions.get(input.threadId);
|
|
80703
|
+
if (existing && !existing.stopped) yield* suspend(() => stopSessionInternal(existing));
|
|
80704
|
+
const runtimeInput = toRuntimeInput(input);
|
|
80517
80705
|
const sessionScope = yield* make$76("sequential");
|
|
80518
80706
|
let sessionScopeTransferred = false;
|
|
80519
80707
|
yield* addFinalizer(() => sessionScopeTransferred ? void_$1 : close(sessionScope, void_$2));
|
|
80520
|
-
const runtime = yield* (
|
|
80708
|
+
const runtime = yield* createRuntime(runtimeInput).pipe(provideService(Scope, sessionScope), provideService(ChildProcessSpawner, childProcessSpawner), provideService(Crypto, crypto), mapError((cause) => new ProviderAdapterProcessError({
|
|
80521
80709
|
provider: PROVIDER,
|
|
80522
80710
|
threadId: input.threadId,
|
|
80523
80711
|
detail: cause.message,
|
|
@@ -80536,7 +80724,7 @@ const makeCodexAdapter = fn("makeCodexAdapter")(function* (codexConfig, options)
|
|
|
80536
80724
|
return;
|
|
80537
80725
|
}
|
|
80538
80726
|
yield* offerAll(runtimeEventQueue, runtimeEvents);
|
|
80539
|
-
})).pipe(
|
|
80727
|
+
})).pipe(forkIn(sessionScope));
|
|
80540
80728
|
const started = yield* runtime.start().pipe(mapError((cause) => new ProviderAdapterProcessError({
|
|
80541
80729
|
provider: PROVIDER,
|
|
80542
80730
|
threadId: input.threadId,
|
|
@@ -80597,20 +80785,24 @@ const makeCodexAdapter = fn("makeCodexAdapter")(function* (codexConfig, options)
|
|
|
80597
80785
|
return session;
|
|
80598
80786
|
});
|
|
80599
80787
|
const interruptTurn = (threadId, turnId) => requireSession(threadId).pipe(flatMap$1((session) => session.runtime.interruptTurn(turnId)), mapError((cause) => cause._tag === "ProviderAdapterSessionNotFoundError" ? cause : mapCodexRuntimeError(threadId, "turn/interrupt", cause)));
|
|
80600
|
-
const readThread = (
|
|
80601
|
-
threadId
|
|
80602
|
-
|
|
80603
|
-
|
|
80788
|
+
const readThread = (input) => {
|
|
80789
|
+
const active = sessions.get(input.threadId);
|
|
80790
|
+
return (active && !active.stopped ? active.runtime.readThread.pipe(mapError((cause) => mapCodexRuntimeError(input.threadId, "thread/read", cause))) : scoped(gen(function* () {
|
|
80791
|
+
return yield* (yield* createRuntime(toRuntimeInput(input)).pipe(provideService(ChildProcessSpawner, childProcessSpawner), provideService(Crypto, crypto), mapError((cause) => new ProviderAdapterProcessError({
|
|
80792
|
+
provider: PROVIDER,
|
|
80793
|
+
threadId: input.threadId,
|
|
80794
|
+
detail: cause.message,
|
|
80795
|
+
cause
|
|
80796
|
+
})))).readThread.pipe(mapError((cause) => mapCodexRuntimeError(input.threadId, "thread/read", cause)));
|
|
80797
|
+
}))).pipe(map$4((snapshot) => mapCodexThreadSnapshot(input.threadId, boundInstanceId, snapshot)));
|
|
80798
|
+
};
|
|
80604
80799
|
const rollbackThread = (threadId, numTurns) => {
|
|
80605
80800
|
if (!Number.isInteger(numTurns) || numTurns < 1) return fail(new ProviderAdapterValidationError({
|
|
80606
80801
|
provider: PROVIDER,
|
|
80607
80802
|
operation: "rollbackThread",
|
|
80608
80803
|
issue: "numTurns must be an integer >= 1."
|
|
80609
80804
|
}));
|
|
80610
|
-
return requireSession(threadId).pipe(flatMap$1((session) => session.runtime.rollbackThread(numTurns)), mapError((cause) => cause._tag === "ProviderAdapterSessionNotFoundError" ? cause : mapCodexRuntimeError(threadId, "thread/rollback", cause)), map$4((snapshot) => (
|
|
80611
|
-
threadId,
|
|
80612
|
-
turns: snapshot.turns
|
|
80613
|
-
})));
|
|
80805
|
+
return requireSession(threadId).pipe(flatMap$1((session) => session.runtime.rollbackThread(numTurns)), mapError((cause) => cause._tag === "ProviderAdapterSessionNotFoundError" ? cause : mapCodexRuntimeError(threadId, "thread/rollback", cause)), map$4((snapshot) => mapCodexThreadSnapshot(threadId, boundInstanceId, snapshot)));
|
|
80614
80806
|
};
|
|
80615
80807
|
const respondToRequest = (threadId, requestId, decision) => requireSession(threadId).pipe(flatMap$1((session) => session.runtime.respondToRequest(requestId, decision)), mapError((cause) => cause._tag === "ProviderAdapterSessionNotFoundError" ? cause : mapCodexRuntimeError(threadId, "item/requestApproval/decision", cause)));
|
|
80616
80808
|
const respondToUserInput = (threadId, requestId, answers) => requireSession(threadId).pipe(flatMap$1((session) => session.runtime.respondToUserInput(requestId, answers)), mapError((cause) => cause._tag === "ProviderAdapterSessionNotFoundError" ? cause : mapCodexRuntimeError(threadId, "item/tool/requestUserInput", cause)));
|
|
@@ -87085,10 +87277,11 @@ function makeAcpAgentAdapter(descriptor, settings, options) {
|
|
|
87085
87277
|
detail: `${descriptor.displayName} ACP sessions do not emit structured user-input requests (request ${requestId}).`
|
|
87086
87278
|
});
|
|
87087
87279
|
});
|
|
87088
|
-
const readThread = (
|
|
87280
|
+
const readThread = (input) => gen(function* () {
|
|
87281
|
+
const ctx = yield* requireSession(input.threadId);
|
|
87089
87282
|
return {
|
|
87090
|
-
threadId,
|
|
87091
|
-
turns:
|
|
87283
|
+
threadId: input.threadId,
|
|
87284
|
+
turns: ctx.turns
|
|
87092
87285
|
};
|
|
87093
87286
|
});
|
|
87094
87287
|
const rollbackThread = (threadId, numTurns) => gen(function* () {
|
|
@@ -87986,20 +88179,6 @@ const ProviderInstanceRegistryHydrationLive = unwrap(gen(function* () {
|
|
|
87986
88179
|
*/
|
|
87987
88180
|
var ProviderCommandReactor = class extends Service$2()("t3/orchestration/Services/ProviderCommandReactor") {};
|
|
87988
88181
|
//#endregion
|
|
87989
|
-
//#region src/orchestration/Services/ProviderRuntimeIngestion.ts
|
|
87990
|
-
/**
|
|
87991
|
-
* ProviderRuntimeIngestionService - Provider runtime ingestion service interface.
|
|
87992
|
-
*
|
|
87993
|
-
* Owns background workers that consume provider runtime streams and emit
|
|
87994
|
-
* orchestration commands/events.
|
|
87995
|
-
*
|
|
87996
|
-
* @module ProviderRuntimeIngestionService
|
|
87997
|
-
*/
|
|
87998
|
-
/**
|
|
87999
|
-
* ProviderRuntimeIngestionService - Service tag for runtime ingestion workers.
|
|
88000
|
-
*/
|
|
88001
|
-
var ProviderRuntimeIngestionService = class extends Service$2()("t3/orchestration/Services/ProviderRuntimeIngestion/ProviderRuntimeIngestionService") {};
|
|
88002
|
-
//#endregion
|
|
88003
88182
|
//#region src/orchestration/Services/ThreadDeletionReactor.ts
|
|
88004
88183
|
/**
|
|
88005
88184
|
* ThreadDeletionReactor - Thread deletion cleanup reactor service interface.
|
|
@@ -88560,7 +88739,18 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, gen
|
|
|
88560
88739
|
const providerService = yield* ProviderService;
|
|
88561
88740
|
const projectionTurnRepository = yield* ProjectionTurnRepository;
|
|
88562
88741
|
const serverSettingsService = yield* ServerSettingsService;
|
|
88563
|
-
const providerCommandId = (event, tag) => crypto.randomUUIDv4.pipe(map$4((uuid) => CommandId.make(`provider:${event.eventId}:${tag}:${uuid}`)));
|
|
88742
|
+
const providerCommandId = (event, tag) => String(event.eventId).startsWith("codex-snapshot:") ? succeed$1(CommandId.make(`provider:${event.eventId}:${tag}`)) : crypto.randomUUIDv4.pipe(map$4((uuid) => CommandId.make(`provider:${event.eventId}:${tag}:${uuid}`)));
|
|
88743
|
+
const threadLocksRef = yield* make$53(/* @__PURE__ */ new Map());
|
|
88744
|
+
const getThreadSemaphore = (threadId) => modifyEffect(threadLocksRef, (current) => {
|
|
88745
|
+
const existing = current.get(threadId);
|
|
88746
|
+
if (existing) return succeed$1([existing, current]);
|
|
88747
|
+
return make$75(1).pipe(map$4((semaphore) => {
|
|
88748
|
+
const next = new Map(current);
|
|
88749
|
+
next.set(threadId, semaphore);
|
|
88750
|
+
return [semaphore, next];
|
|
88751
|
+
}));
|
|
88752
|
+
});
|
|
88753
|
+
const withThreadLock = (threadId, effect) => flatMap$1(getThreadSemaphore(threadId), (semaphore) => semaphore.withPermit(effect));
|
|
88564
88754
|
const turnMessageIdsByTurnKey = yield* make$81({
|
|
88565
88755
|
capacity: TURN_MESSAGE_IDS_BY_TURN_CACHE_CAPACITY,
|
|
88566
88756
|
timeToLive: TURN_MESSAGE_IDS_BY_TURN_TTL,
|
|
@@ -88679,7 +88869,7 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, gen
|
|
|
88679
88869
|
if (!hasRenderableAssistantText(bufferedText)) return false;
|
|
88680
88870
|
yield* orchestrationEngine.dispatch({
|
|
88681
88871
|
type: "thread.message.assistant.delta",
|
|
88682
|
-
commandId: yield* providerCommandId(input.event, input.commandTag),
|
|
88872
|
+
commandId: yield* providerCommandId(input.event, `${input.commandTag}:${input.messageId}`),
|
|
88683
88873
|
threadId: input.threadId,
|
|
88684
88874
|
messageId: input.messageId,
|
|
88685
88875
|
delta: bufferedText,
|
|
@@ -88707,7 +88897,7 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, gen
|
|
|
88707
88897
|
const hasRenderableText = hasRenderableAssistantText(text);
|
|
88708
88898
|
if (hasRenderableText) yield* orchestrationEngine.dispatch({
|
|
88709
88899
|
type: "thread.message.assistant.delta",
|
|
88710
|
-
commandId: yield* providerCommandId(input.event, input.finalDeltaCommandTag),
|
|
88900
|
+
commandId: yield* providerCommandId(input.event, `${input.finalDeltaCommandTag}:${input.messageId}`),
|
|
88711
88901
|
threadId: input.threadId,
|
|
88712
88902
|
messageId: input.messageId,
|
|
88713
88903
|
delta: text,
|
|
@@ -88716,7 +88906,7 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, gen
|
|
|
88716
88906
|
});
|
|
88717
88907
|
if (input.hasProjectedMessage || hasRenderableText) yield* orchestrationEngine.dispatch({
|
|
88718
88908
|
type: "thread.message.assistant.complete",
|
|
88719
|
-
commandId: yield* providerCommandId(input.event, input.commandTag),
|
|
88909
|
+
commandId: yield* providerCommandId(input.event, `${input.commandTag}:${input.messageId}`),
|
|
88720
88910
|
threadId: input.threadId,
|
|
88721
88911
|
messageId: input.messageId,
|
|
88722
88912
|
...input.turnId ? { turnId: input.turnId } : {},
|
|
@@ -88750,7 +88940,7 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, gen
|
|
|
88750
88940
|
const existingPlan = findProposedPlanById(input.threadProposedPlans, input.planId);
|
|
88751
88941
|
yield* orchestrationEngine.dispatch({
|
|
88752
88942
|
type: "thread.proposed-plan.upsert",
|
|
88753
|
-
commandId: yield* providerCommandId(input.event,
|
|
88943
|
+
commandId: yield* providerCommandId(input.event, `proposed-plan-upsert:${input.planId}`),
|
|
88754
88944
|
threadId: input.threadId,
|
|
88755
88945
|
proposedPlan: {
|
|
88756
88946
|
id: input.planId,
|
|
@@ -89094,7 +89284,7 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, gen
|
|
|
89094
89284
|
taskTitle = yield* lookupTaskDescription(thread.id, event.payload.taskId);
|
|
89095
89285
|
if (!taskTitle) taskTitle = findTaskTitleInActivities((yield* getLoadedThreadDetail())?.activities, event.payload.taskId);
|
|
89096
89286
|
}
|
|
89097
|
-
yield* forEach(runtimeEventToActivities(event, taskTitle), (activity) => providerCommandId(event,
|
|
89287
|
+
yield* forEach(runtimeEventToActivities(event, taskTitle), (activity) => providerCommandId(event, `thread-activity-append:${activity.id}`).pipe(flatMap$1((commandId) => orchestrationEngine.dispatch({
|
|
89098
89288
|
type: "thread.activity.append",
|
|
89099
89289
|
commandId,
|
|
89100
89290
|
threadId: thread.id,
|
|
@@ -89102,6 +89292,51 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, gen
|
|
|
89102
89292
|
createdAt: activity.createdAt
|
|
89103
89293
|
})))).pipe(asVoid);
|
|
89104
89294
|
});
|
|
89295
|
+
const reconcileThreadUnlocked = fn("reconcileThreadUnlocked")(function* (threadId, options) {
|
|
89296
|
+
const thread = yield* resolveThreadShell(threadId);
|
|
89297
|
+
if (!thread || thread.session?.providerName !== ProviderDriverKind.make("codex")) return;
|
|
89298
|
+
const snapshot = yield* providerService.readThread(threadId);
|
|
89299
|
+
for (const turn of snapshot.turns) {
|
|
89300
|
+
const existing = getOrUndefined(yield* projectionTurnRepository.getByTurnId({
|
|
89301
|
+
threadId,
|
|
89302
|
+
turnId: turn.id
|
|
89303
|
+
}));
|
|
89304
|
+
const isTerminal = turn.status === "completed" || turn.status === "failed" || turn.status === "interrupted";
|
|
89305
|
+
if (isTerminal && existing && (existing.state === "completed" || existing.state === "error" || existing.state === "interrupted")) continue;
|
|
89306
|
+
if (isTerminal) {
|
|
89307
|
+
const pendingTurnStart = yield* projectionTurnRepository.getPendingTurnStartByThreadId({ threadId });
|
|
89308
|
+
if (!(existing?.pendingMessageId !== null && existing?.pendingMessageId !== void 0 ? true : isSome(pendingTurnStart))) {
|
|
89309
|
+
const createdAt = turn.startedAt ?? turn.runtimeEvents?.[0]?.createdAt ?? thread.session.updatedAt;
|
|
89310
|
+
const providerThreadId = snapshot.providerThreadId ?? String(threadId);
|
|
89311
|
+
for (const userMessage of turn.userMessages ?? []) yield* orchestrationEngine.dispatch({
|
|
89312
|
+
type: "thread.message.user.import",
|
|
89313
|
+
commandId: CommandId.make(`provider-user:${providerThreadId}:${turn.id}:${userMessage.id}:import`),
|
|
89314
|
+
threadId,
|
|
89315
|
+
messageId: MessageId.make(`provider-user:${providerThreadId}:${turn.id}:${userMessage.id}`),
|
|
89316
|
+
turnId: turn.id,
|
|
89317
|
+
text: userMessage.text,
|
|
89318
|
+
createdAt
|
|
89319
|
+
});
|
|
89320
|
+
}
|
|
89321
|
+
}
|
|
89322
|
+
const runtimeEvents = turn.runtimeEvents ?? [];
|
|
89323
|
+
yield* forEach(isTerminal ? runtimeEvents : runtimeEvents.filter((event) => event.type === "turn.started"), processRuntimeEvent, {
|
|
89324
|
+
concurrency: 1,
|
|
89325
|
+
discard: true
|
|
89326
|
+
});
|
|
89327
|
+
}
|
|
89328
|
+
if (options?.requireWritable !== true) return;
|
|
89329
|
+
if (snapshot.status !== "idle" && snapshot.status !== "notLoaded" || snapshot.turns.some((turn) => turn.status === "inProgress")) return yield* new ProviderRuntimeReconciliationError({
|
|
89330
|
+
threadId,
|
|
89331
|
+
detail: snapshot.status === "active" ? "Codex is still processing this thread; wait for it to finish before sending." : `Codex thread is not writable while its native status is '${snapshot.status ?? "unknown"}'.`
|
|
89332
|
+
});
|
|
89333
|
+
yield* providerService.ensureSession(threadId);
|
|
89334
|
+
});
|
|
89335
|
+
const reconcileThread = (threadId, options) => withThreadLock(threadId, reconcileThreadUnlocked(threadId, options)).pipe(mapError((cause) => cause instanceof ProviderRuntimeReconciliationError ? cause : new ProviderRuntimeReconciliationError({
|
|
89336
|
+
threadId,
|
|
89337
|
+
detail: cause instanceof Error ? cause.message : "Provider snapshot sync failed.",
|
|
89338
|
+
cause
|
|
89339
|
+
})));
|
|
89105
89340
|
const processDomainEvent = (_event) => void_$1;
|
|
89106
89341
|
const processInput = (input) => input.source === "runtime" ? processRuntimeEvent(input.event) : processDomainEvent(input.event);
|
|
89107
89342
|
const processInputSafely = (input) => processInput(input).pipe(catchCause((cause) => {
|
|
@@ -89129,6 +89364,7 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, gen
|
|
|
89129
89364
|
});
|
|
89130
89365
|
return {
|
|
89131
89366
|
start,
|
|
89367
|
+
reconcileThread,
|
|
89132
89368
|
drain: worker.drain
|
|
89133
89369
|
};
|
|
89134
89370
|
})).pipe(provide(ProjectionTurnRepositoryLive));
|