t3code-cli 0.1.1 → 0.1.3
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/dist/bin.js +2 -2
- package/dist/index.js +1 -1
- package/dist/{runtime-DU_hs0MM.js → runtime-0wuYCEoH.js} +9952 -2871
- package/package.json +5 -3
- package/src/application/model-selection.ts +13 -15
- package/src/application/models.ts +1 -1
- package/src/application/project-commands.ts +6 -4
- package/src/application/service.ts +26 -22
- package/src/application/shell-sequence.ts +1 -1
- package/src/application/thread-commands.ts +32 -22
- package/src/application/thread-wait.ts +4 -4
- package/src/cli/model-format.ts +1 -1
- package/src/cli/project-format.ts +3 -3
- package/src/cli/thread-format.ts +6 -6
- package/src/domain/helpers.ts +11 -3
- package/src/domain/model-config.ts +1 -1
- package/src/domain/thread-lifecycle.ts +24 -30
- package/src/index.ts +6 -6
- package/src/orchestration/layer.ts +41 -48
- package/src/orchestration/service.ts +20 -11
- package/src/rpc/error.ts +12 -0
- package/src/rpc/layer.ts +2 -2
- package/src/rpc/service.ts +2 -3
- package/src/rpc/ws-group.ts +43 -0
- package/src/domain/command-schema.ts +0 -82
- package/src/domain/schema.ts +0 -162
- package/src/protocol/schema.ts +0 -105
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "t3code-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "CLI for t3code",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -32,13 +32,15 @@
|
|
|
32
32
|
"types": "./src/index.ts"
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
|
+
"dependencies": {},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@changesets/cli": "^2.31.0",
|
|
37
38
|
"@effect/platform-node": "4.0.0-beta.74",
|
|
38
39
|
"@types/node": "^25.9.1",
|
|
39
40
|
"effect": "4.0.0-beta.74",
|
|
40
41
|
"typescript": "^6.0.3",
|
|
41
|
-
"vite-plus": "^0.1.24"
|
|
42
|
+
"vite-plus": "^0.1.24",
|
|
43
|
+
"@t3tools/contracts": "0.0.24"
|
|
42
44
|
},
|
|
43
45
|
"engines": {
|
|
44
46
|
"node": "^24.0.0"
|
|
@@ -52,7 +54,7 @@
|
|
|
52
54
|
"lint:fix": "vp lint --fix",
|
|
53
55
|
"changeset": "changeset",
|
|
54
56
|
"release:check": "pnpm check && pnpm typecheck && pnpm pack --dry-run",
|
|
55
|
-
"release:publish": "
|
|
57
|
+
"release:publish": "changeset publish",
|
|
56
58
|
"typecheck": "tsc --noEmit"
|
|
57
59
|
}
|
|
58
60
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
|
+
import { ModelSelection, ProviderInstanceId } from "@t3tools/contracts";
|
|
3
|
+
import type { OrchestrationProjectShell } from "@t3tools/contracts";
|
|
4
|
+
import * as Schema from "effect/Schema";
|
|
2
5
|
|
|
3
6
|
import { ModelSelectionError } from "../domain/error.ts";
|
|
4
7
|
import {
|
|
@@ -6,23 +9,18 @@ import {
|
|
|
6
9
|
firstSelectableModel,
|
|
7
10
|
firstSelectableProvider,
|
|
8
11
|
} from "../domain/model-config.ts";
|
|
9
|
-
import {
|
|
10
|
-
decodeModelSelection,
|
|
11
|
-
type ModelSelection,
|
|
12
|
-
type ProjectShell,
|
|
13
|
-
type ServerConfig,
|
|
14
|
-
} from "../domain/schema.ts";
|
|
12
|
+
import type { ServerConfigForCli } from "../orchestration/service.ts";
|
|
15
13
|
import type { StartThreadInput } from "./service.ts";
|
|
16
14
|
|
|
17
15
|
export function resolveModelSelection(input: {
|
|
18
16
|
readonly start: StartThreadInput;
|
|
19
|
-
readonly project:
|
|
20
|
-
readonly serverConfig:
|
|
17
|
+
readonly project: OrchestrationProjectShell;
|
|
18
|
+
readonly serverConfig: ServerConfigForCli;
|
|
21
19
|
}) {
|
|
22
20
|
return Effect.gen(function* () {
|
|
23
21
|
if (input.start.provider !== undefined && input.start.model !== undefined) {
|
|
24
22
|
return withModelOptions(input.start, {
|
|
25
|
-
instanceId: input.start.provider,
|
|
23
|
+
instanceId: ProviderInstanceId.make(input.start.provider),
|
|
26
24
|
model: input.start.model,
|
|
27
25
|
});
|
|
28
26
|
}
|
|
@@ -33,7 +31,7 @@ export function resolveModelSelection(input: {
|
|
|
33
31
|
return yield* failNoAvailableModel();
|
|
34
32
|
}
|
|
35
33
|
return withModelOptions(input.start, {
|
|
36
|
-
instanceId: input.start.provider,
|
|
34
|
+
instanceId: ProviderInstanceId.make(input.start.provider),
|
|
37
35
|
model: model.slug,
|
|
38
36
|
});
|
|
39
37
|
}
|
|
@@ -70,7 +68,7 @@ export function mergeModelOptions(
|
|
|
70
68
|
for (const option of options) {
|
|
71
69
|
optionsById.set(option.id, option);
|
|
72
70
|
}
|
|
73
|
-
return
|
|
71
|
+
return Schema.decodeUnknownSync(ModelSelection)({
|
|
74
72
|
...selection,
|
|
75
73
|
options: [...optionsById.values()],
|
|
76
74
|
});
|
|
@@ -83,8 +81,8 @@ function withModelOptions(input: StartThreadInput, selection: ModelSelection): M
|
|
|
83
81
|
return mergeModelOptions(selection, input.options);
|
|
84
82
|
}
|
|
85
83
|
|
|
86
|
-
function firstAvailableModel(serverConfig:
|
|
87
|
-
const providers = serverConfig.providers
|
|
84
|
+
function firstAvailableModel(serverConfig: ServerConfigForCli) {
|
|
85
|
+
const providers = serverConfig.providers;
|
|
88
86
|
const provider = firstSelectableProvider(providers);
|
|
89
87
|
if (provider === undefined) {
|
|
90
88
|
return failNoAvailableModel();
|
|
@@ -92,8 +90,8 @@ function firstAvailableModel(serverConfig: ServerConfig) {
|
|
|
92
90
|
return Effect.succeed(provider);
|
|
93
91
|
}
|
|
94
92
|
|
|
95
|
-
function findProvider(serverConfig:
|
|
96
|
-
const provider = findSelectableProvider(serverConfig.providers
|
|
93
|
+
function findProvider(serverConfig: ServerConfigForCli, instanceId: string) {
|
|
94
|
+
const provider = findSelectableProvider(serverConfig.providers, instanceId);
|
|
97
95
|
if (provider === undefined) {
|
|
98
96
|
return failNoAvailableModel();
|
|
99
97
|
}
|
|
@@ -12,7 +12,7 @@ export const makeModelsApplication = Effect.fn("makeModelsApplication")(function
|
|
|
12
12
|
}) {
|
|
13
13
|
const config = yield* orchestration.getServerConfig();
|
|
14
14
|
return filterProvidersForModelListing({
|
|
15
|
-
providers: config.providers
|
|
15
|
+
providers: config.providers,
|
|
16
16
|
all: input.all === true,
|
|
17
17
|
...(input.provider !== undefined && input.provider.length > 0
|
|
18
18
|
? { provider: input.provider }
|
|
@@ -2,9 +2,9 @@ import * as Crypto from "effect/Crypto";
|
|
|
2
2
|
import * as DateTime from "effect/DateTime";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
4
|
import * as Path from "effect/Path";
|
|
5
|
+
import { CommandId, ProjectId, type ClientOrchestrationCommand } from "@t3tools/contracts";
|
|
5
6
|
|
|
6
7
|
import { Environment } from "../environment/service.ts";
|
|
7
|
-
import type { ProjectCreateCommand } from "../domain/command-schema.ts";
|
|
8
8
|
|
|
9
9
|
export const makeProjectCreateCommand = Effect.fn("makeProjectCreateCommand")(function* (input: {
|
|
10
10
|
readonly path: string;
|
|
@@ -14,15 +14,17 @@ export const makeProjectCreateCommand = Effect.fn("makeProjectCreateCommand")(fu
|
|
|
14
14
|
const crypto = yield* Crypto.Crypto;
|
|
15
15
|
const environment = yield* Environment;
|
|
16
16
|
const workspaceRoot = path.resolve(environment.cwd, input.path);
|
|
17
|
-
const projectId = yield* crypto.randomUUIDv4.pipe(Effect.orDie);
|
|
17
|
+
const projectId = ProjectId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie));
|
|
18
18
|
const title = input.title?.trim();
|
|
19
19
|
const createdAt = DateTime.formatIso(yield* DateTime.now);
|
|
20
20
|
return {
|
|
21
21
|
type: "project.create",
|
|
22
|
-
commandId:
|
|
22
|
+
commandId: CommandId.make(
|
|
23
|
+
`t3cli:project-create:${yield* crypto.randomUUIDv4.pipe(Effect.orDie)}`,
|
|
24
|
+
),
|
|
23
25
|
projectId,
|
|
24
26
|
title: title !== undefined && title.length > 0 ? title : path.basename(workspaceRoot),
|
|
25
27
|
workspaceRoot,
|
|
26
28
|
createdAt,
|
|
27
|
-
} satisfies
|
|
29
|
+
} satisfies Extract<ClientOrchestrationCommand, { readonly type: "project.create" }>;
|
|
28
30
|
});
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import * as Context from "effect/Context";
|
|
2
2
|
import type * as Effect from "effect/Effect";
|
|
3
3
|
import type * as Stream from "effect/Stream";
|
|
4
|
-
|
|
5
|
-
import type { ApplicationError } from "./error.ts";
|
|
6
|
-
import type { DispatchResult } from "../domain/command-schema.ts";
|
|
7
4
|
import type {
|
|
8
|
-
|
|
9
|
-
ServerProvider,
|
|
10
|
-
ShellSnapshot,
|
|
11
|
-
ThreadDetail,
|
|
12
|
-
ThreadMessage,
|
|
13
|
-
ThreadShell,
|
|
5
|
+
DispatchResult,
|
|
14
6
|
ModelSelection,
|
|
15
|
-
|
|
7
|
+
OrchestrationMessage,
|
|
8
|
+
OrchestrationProjectShell,
|
|
9
|
+
OrchestrationShellSnapshot,
|
|
10
|
+
OrchestrationThread,
|
|
11
|
+
OrchestrationThreadShell,
|
|
12
|
+
ServerProvider,
|
|
13
|
+
} from "@t3tools/contracts";
|
|
14
|
+
|
|
15
|
+
import type { ApplicationError } from "./error.ts";
|
|
16
16
|
|
|
17
17
|
export type StartThreadInput = {
|
|
18
18
|
readonly projectRef: string;
|
|
@@ -35,15 +35,15 @@ export type StartThreadPolicy = {
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
export type WaitEvent =
|
|
38
|
-
| { readonly type: "thread"; readonly thread:
|
|
39
|
-
| { readonly type: "message"; readonly message:
|
|
38
|
+
| { readonly type: "thread"; readonly thread: OrchestrationThread }
|
|
39
|
+
| { readonly type: "message"; readonly message: OrchestrationMessage }
|
|
40
40
|
| { readonly type: "status"; readonly status: string; readonly threadId: string }
|
|
41
|
-
| { readonly type: "done"; readonly thread:
|
|
41
|
+
| { readonly type: "done"; readonly thread: OrchestrationThread };
|
|
42
42
|
|
|
43
43
|
export class T3Application extends Context.Service<
|
|
44
44
|
T3Application,
|
|
45
45
|
{
|
|
46
|
-
readonly loadShell: () => Effect.Effect<
|
|
46
|
+
readonly loadShell: () => Effect.Effect<OrchestrationShellSnapshot, ApplicationError>;
|
|
47
47
|
readonly listModels: (input: {
|
|
48
48
|
readonly all?: boolean;
|
|
49
49
|
readonly provider?: string;
|
|
@@ -52,17 +52,19 @@ export class T3Application extends Context.Service<
|
|
|
52
52
|
readonly path: string;
|
|
53
53
|
readonly title?: string;
|
|
54
54
|
}) => Effect.Effect<
|
|
55
|
-
{ readonly dispatch: DispatchResult; readonly project:
|
|
55
|
+
{ readonly dispatch: DispatchResult; readonly project: OrchestrationProjectShell },
|
|
56
56
|
ApplicationError
|
|
57
57
|
>;
|
|
58
58
|
readonly listThreads: (projectRef: string) => Effect.Effect<
|
|
59
59
|
{
|
|
60
|
-
readonly project:
|
|
61
|
-
readonly threads: ReadonlyArray<
|
|
60
|
+
readonly project: OrchestrationProjectShell;
|
|
61
|
+
readonly threads: ReadonlyArray<OrchestrationThreadShell>;
|
|
62
62
|
},
|
|
63
63
|
ApplicationError
|
|
64
64
|
>;
|
|
65
|
-
readonly getThreadMessages: (
|
|
65
|
+
readonly getThreadMessages: (
|
|
66
|
+
threadId: string,
|
|
67
|
+
) => Effect.Effect<OrchestrationThread, ApplicationError>;
|
|
66
68
|
readonly archiveThread: (threadId: string) => Effect.Effect<DispatchResult, ApplicationError>;
|
|
67
69
|
readonly startThread: (
|
|
68
70
|
input: StartThreadInput,
|
|
@@ -70,9 +72,9 @@ export class T3Application extends Context.Service<
|
|
|
70
72
|
) => Effect.Effect<
|
|
71
73
|
{
|
|
72
74
|
readonly dispatch: DispatchResult;
|
|
73
|
-
readonly project:
|
|
75
|
+
readonly project: OrchestrationProjectShell;
|
|
74
76
|
readonly threadId: string;
|
|
75
|
-
readonly thread?:
|
|
77
|
+
readonly thread?: OrchestrationThread;
|
|
76
78
|
},
|
|
77
79
|
ApplicationError
|
|
78
80
|
>;
|
|
@@ -83,11 +85,13 @@ export class T3Application extends Context.Service<
|
|
|
83
85
|
{
|
|
84
86
|
readonly dispatch: DispatchResult;
|
|
85
87
|
readonly threadId: string;
|
|
86
|
-
readonly thread?:
|
|
88
|
+
readonly thread?: OrchestrationThread;
|
|
87
89
|
},
|
|
88
90
|
ApplicationError
|
|
89
91
|
>;
|
|
90
92
|
readonly watchThread: (threadId: string) => Stream.Stream<WaitEvent, ApplicationError>;
|
|
91
|
-
readonly waitForThread: (
|
|
93
|
+
readonly waitForThread: (
|
|
94
|
+
threadId: string,
|
|
95
|
+
) => Effect.Effect<OrchestrationThread, ApplicationError>;
|
|
92
96
|
}
|
|
93
97
|
>()("t3cli/T3Application") {}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Option from "effect/Option";
|
|
3
3
|
import * as Stream from "effect/Stream";
|
|
4
|
+
import { ORCHESTRATION_WS_METHODS } from "@t3tools/contracts";
|
|
4
5
|
|
|
5
6
|
import type { Orchestration } from "../orchestration/service.ts";
|
|
6
7
|
import { RpcError } from "../rpc/error.ts";
|
|
7
|
-
import { ORCHESTRATION_WS_METHODS } from "../protocol/schema.ts";
|
|
8
8
|
|
|
9
9
|
export function waitForShellSequence(input: {
|
|
10
10
|
readonly orchestration: Orchestration;
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import * as Crypto from "effect/Crypto";
|
|
2
2
|
import * as DateTime from "effect/DateTime";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
import {
|
|
5
|
+
CommandId,
|
|
6
|
+
MessageId,
|
|
7
|
+
ThreadId,
|
|
8
|
+
type ClientOrchestrationCommand,
|
|
9
|
+
type ModelSelection,
|
|
10
|
+
type OrchestrationProjectShell,
|
|
11
|
+
} from "@t3tools/contracts";
|
|
12
|
+
import type { ServerConfigForCli } from "../orchestration/service.ts";
|
|
11
13
|
import { resolveModelSelection } from "./model-selection.ts";
|
|
12
14
|
import type { SendThreadInput, StartThreadInput } from "./service.ts";
|
|
13
15
|
|
|
14
16
|
export const makeThreadStartCommands = Effect.fn("makeThreadStartCommands")(function* (input: {
|
|
15
17
|
readonly start: StartThreadInput;
|
|
16
|
-
readonly project:
|
|
17
|
-
readonly serverConfig:
|
|
18
|
+
readonly project: OrchestrationProjectShell;
|
|
19
|
+
readonly serverConfig: ServerConfigForCli;
|
|
18
20
|
}) {
|
|
19
21
|
const crypto = yield* Crypto.Crypto;
|
|
20
|
-
const threadId = yield* crypto.randomUUIDv4.pipe(Effect.orDie);
|
|
22
|
+
const threadId = ThreadId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie));
|
|
21
23
|
const createdAt = DateTime.formatIso(yield* DateTime.now);
|
|
22
24
|
const modelSelection = yield* resolveModelSelection(input);
|
|
23
25
|
const inputTitle = input.start.title?.trim();
|
|
@@ -25,7 +27,9 @@ export const makeThreadStartCommands = Effect.fn("makeThreadStartCommands")(func
|
|
|
25
27
|
const title = inputTitle !== undefined && inputTitle.length > 0 ? inputTitle : messageTitle;
|
|
26
28
|
const createCommand = {
|
|
27
29
|
type: "thread.create",
|
|
28
|
-
commandId:
|
|
30
|
+
commandId: CommandId.make(
|
|
31
|
+
`t3cli:thread-create:${yield* crypto.randomUUIDv4.pipe(Effect.orDie)}`,
|
|
32
|
+
),
|
|
29
33
|
threadId,
|
|
30
34
|
projectId: input.project.id,
|
|
31
35
|
title: title.length > 0 ? title : "New thread",
|
|
@@ -35,13 +39,15 @@ export const makeThreadStartCommands = Effect.fn("makeThreadStartCommands")(func
|
|
|
35
39
|
branch: null,
|
|
36
40
|
worktreePath: input.start.worktreePath ?? null,
|
|
37
41
|
createdAt,
|
|
38
|
-
} satisfies
|
|
42
|
+
} satisfies Extract<ClientOrchestrationCommand, { readonly type: "thread.create" }>;
|
|
39
43
|
const turnCommand = {
|
|
40
44
|
type: "thread.turn.start",
|
|
41
|
-
commandId:
|
|
45
|
+
commandId: CommandId.make(
|
|
46
|
+
`t3cli:thread-start:${yield* crypto.randomUUIDv4.pipe(Effect.orDie)}`,
|
|
47
|
+
),
|
|
42
48
|
threadId,
|
|
43
49
|
message: {
|
|
44
|
-
messageId: yield* crypto.randomUUIDv4.pipe(Effect.orDie),
|
|
50
|
+
messageId: MessageId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie)),
|
|
45
51
|
role: "user",
|
|
46
52
|
text: input.start.message,
|
|
47
53
|
attachments: [],
|
|
@@ -51,7 +57,7 @@ export const makeThreadStartCommands = Effect.fn("makeThreadStartCommands")(func
|
|
|
51
57
|
runtimeMode: "full-access",
|
|
52
58
|
interactionMode: "default",
|
|
53
59
|
createdAt,
|
|
54
|
-
} satisfies
|
|
60
|
+
} satisfies Extract<ClientOrchestrationCommand, { readonly type: "thread.turn.start" }>;
|
|
55
61
|
return { createCommand, turnCommand, threadId };
|
|
56
62
|
});
|
|
57
63
|
|
|
@@ -62,10 +68,12 @@ export const makeThreadTurnContinueCommand = Effect.fn("makeThreadTurnContinueCo
|
|
|
62
68
|
const createdAt = DateTime.formatIso(yield* DateTime.now);
|
|
63
69
|
return {
|
|
64
70
|
type: "thread.turn.start",
|
|
65
|
-
commandId:
|
|
66
|
-
|
|
71
|
+
commandId: CommandId.make(
|
|
72
|
+
`t3cli:thread-start:${yield* crypto.randomUUIDv4.pipe(Effect.orDie)}`,
|
|
73
|
+
),
|
|
74
|
+
threadId: ThreadId.make(input.threadId),
|
|
67
75
|
message: {
|
|
68
|
-
messageId: yield* crypto.randomUUIDv4.pipe(Effect.orDie),
|
|
76
|
+
messageId: MessageId.make(yield* crypto.randomUUIDv4.pipe(Effect.orDie)),
|
|
69
77
|
role: "user",
|
|
70
78
|
text: input.message,
|
|
71
79
|
attachments: [],
|
|
@@ -74,7 +82,7 @@ export const makeThreadTurnContinueCommand = Effect.fn("makeThreadTurnContinueCo
|
|
|
74
82
|
interactionMode: "default",
|
|
75
83
|
...(input.modelSelection !== undefined ? { modelSelection: input.modelSelection } : {}),
|
|
76
84
|
createdAt,
|
|
77
|
-
} satisfies
|
|
85
|
+
} satisfies Extract<ClientOrchestrationCommand, { readonly type: "thread.turn.start" }>;
|
|
78
86
|
});
|
|
79
87
|
|
|
80
88
|
export const makeThreadArchiveCommand = Effect.fn("makeThreadArchiveCommand")(function* (
|
|
@@ -83,7 +91,9 @@ export const makeThreadArchiveCommand = Effect.fn("makeThreadArchiveCommand")(fu
|
|
|
83
91
|
const crypto = yield* Crypto.Crypto;
|
|
84
92
|
return {
|
|
85
93
|
type: "thread.archive",
|
|
86
|
-
commandId:
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
commandId: CommandId.make(
|
|
95
|
+
`t3cli:thread-archive:${yield* crypto.randomUUIDv4.pipe(Effect.orDie)}`,
|
|
96
|
+
),
|
|
97
|
+
threadId: ThreadId.make(threadId),
|
|
98
|
+
} satisfies Extract<ClientOrchestrationCommand, { readonly type: "thread.archive" }>;
|
|
89
99
|
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Option from "effect/Option";
|
|
3
3
|
import * as Stream from "effect/Stream";
|
|
4
|
+
import type { OrchestrationMessage, OrchestrationThread } from "@t3tools/contracts";
|
|
4
5
|
|
|
5
6
|
import { ThreadSessionError } from "../domain/error.ts";
|
|
6
7
|
import type { Orchestration } from "../orchestration/service.ts";
|
|
7
|
-
import type { ThreadDetail, ThreadMessage } from "../domain/schema.ts";
|
|
8
8
|
import type { WaitEvent } from "./service.ts";
|
|
9
9
|
import {
|
|
10
10
|
applyThreadEvent,
|
|
@@ -19,13 +19,13 @@ export function watchThread(input: {
|
|
|
19
19
|
readonly orchestration: Orchestration;
|
|
20
20
|
readonly threadId: string;
|
|
21
21
|
}) {
|
|
22
|
-
let current:
|
|
23
|
-
let currentMessages: Map<string,
|
|
22
|
+
let current: OrchestrationThread | undefined;
|
|
23
|
+
let currentMessages: Map<string, OrchestrationMessage> | undefined;
|
|
24
24
|
return Stream.scoped(
|
|
25
25
|
input.orchestration.watchThreadItems(input.threadId).pipe(
|
|
26
26
|
Stream.flatMap((item) => {
|
|
27
27
|
if (item.kind === "snapshot") {
|
|
28
|
-
const messages = new Map<string,
|
|
28
|
+
const messages = new Map<string, OrchestrationMessage>();
|
|
29
29
|
for (const message of item.snapshot.thread.messages) {
|
|
30
30
|
messages.set(messageKey(message), message);
|
|
31
31
|
}
|
package/src/cli/model-format.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { OrchestrationProjectShell } from "@t3tools/contracts";
|
|
2
2
|
|
|
3
|
-
export function formatProjectsHuman(projects: ReadonlyArray<
|
|
3
|
+
export function formatProjectsHuman(projects: ReadonlyArray<OrchestrationProjectShell>) {
|
|
4
4
|
return projects
|
|
5
5
|
.map((project) => `- ${project.title}\n id: ${project.id}\n path: ${project.workspaceRoot}\n`)
|
|
6
6
|
.join("");
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
export function formatProjectAddedHuman(project:
|
|
9
|
+
export function formatProjectAddedHuman(project: OrchestrationProjectShell) {
|
|
10
10
|
return `project added: ${project.title}\nid: ${project.id}\npath: ${project.workspaceRoot}`;
|
|
11
11
|
}
|
package/src/cli/thread-format.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { WaitEvent } from "../application/service.ts";
|
|
2
|
-
import type {
|
|
2
|
+
import type { OrchestrationThread, OrchestrationThreadShell } from "@t3tools/contracts";
|
|
3
3
|
import { latestAssistantMessage, threadStatus } from "../domain/thread-lifecycle.ts";
|
|
4
4
|
|
|
5
|
-
export function formatThreadsHuman(threads: ReadonlyArray<
|
|
5
|
+
export function formatThreadsHuman(threads: ReadonlyArray<OrchestrationThreadShell>) {
|
|
6
6
|
return threads
|
|
7
7
|
.map(
|
|
8
8
|
(thread) =>
|
|
@@ -12,23 +12,23 @@ export function formatThreadsHuman(threads: ReadonlyArray<ThreadShell>) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function formatThreadStartedHuman(input: {
|
|
15
|
-
readonly thread:
|
|
15
|
+
readonly thread: OrchestrationThread;
|
|
16
16
|
readonly sequence: number;
|
|
17
17
|
}) {
|
|
18
18
|
return `thread started: ${input.thread.title}\nid: ${input.thread.id}\nstatus: ${threadStatus(input.thread)}\nsequence: ${input.sequence}`;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export function formatThreadMessagesHuman(thread:
|
|
21
|
+
export function formatThreadMessagesHuman(thread: OrchestrationThread, limit: number) {
|
|
22
22
|
const messages = limit === 0 ? thread.messages : thread.messages.slice(-limit);
|
|
23
23
|
return messages.map((message) => `\n### ${message.role}\n\n${message.text}\n`).join("");
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export function formatWaitDoneHuman(thread:
|
|
26
|
+
export function formatWaitDoneHuman(thread: OrchestrationThread) {
|
|
27
27
|
const latest = latestAssistantMessage(thread);
|
|
28
28
|
return `status: ${threadStatus(thread)}\n${latest !== undefined ? `\n### ${latest.role}\n\n${latest.text}\n` : ""}`;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export function formatThreadMessagesJson(thread:
|
|
31
|
+
export function formatThreadMessagesJson(thread: OrchestrationThread, full: boolean) {
|
|
32
32
|
return full ? thread : { thread: stripThreadMessages(thread), messages: thread.messages };
|
|
33
33
|
}
|
|
34
34
|
|
package/src/domain/helpers.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import type * as Path from "effect/Path";
|
|
2
|
+
import type { OrchestrationProjectShell, OrchestrationShellSnapshot } from "@t3tools/contracts";
|
|
2
3
|
|
|
3
4
|
import { ProjectLookupError } from "./error.ts";
|
|
4
|
-
import type { ProjectShell, ShellSnapshot } from "./schema.ts";
|
|
5
5
|
|
|
6
|
-
export function resolveProject(
|
|
6
|
+
export function resolveProject(
|
|
7
|
+
snapshot: OrchestrationShellSnapshot,
|
|
8
|
+
ref: string,
|
|
9
|
+
path: Path.Path,
|
|
10
|
+
cwd: string,
|
|
11
|
+
) {
|
|
7
12
|
const byId = findProjectById(snapshot, ref);
|
|
8
13
|
if (byId !== null) {
|
|
9
14
|
return byId;
|
|
@@ -18,6 +23,9 @@ export function resolveProject(snapshot: ShellSnapshot, ref: string, path: Path.
|
|
|
18
23
|
throw new ProjectLookupError({ message: `project not found: ${ref}`, ref });
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
export function findProjectById(
|
|
26
|
+
export function findProjectById(
|
|
27
|
+
snapshot: OrchestrationShellSnapshot,
|
|
28
|
+
projectId: string,
|
|
29
|
+
): OrchestrationProjectShell | null {
|
|
22
30
|
return snapshot.projects.find((project) => project.id === projectId) ?? null;
|
|
23
31
|
}
|
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
import * as Schema from "effect/Schema";
|
|
2
|
-
|
|
3
1
|
import {
|
|
4
|
-
|
|
5
|
-
type
|
|
6
|
-
type
|
|
7
|
-
type
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from "./schema.ts";
|
|
12
|
-
|
|
13
|
-
const isThreadMessageSentEvent = Schema.is(ThreadMessageSentEventSchema);
|
|
14
|
-
const isThreadSessionSetEvent = Schema.is(ThreadSessionSetEventSchema);
|
|
2
|
+
OrchestrationMessage,
|
|
3
|
+
type OrchestrationEvent,
|
|
4
|
+
type OrchestrationMessage as OrchestrationMessageType,
|
|
5
|
+
type OrchestrationThread,
|
|
6
|
+
type OrchestrationThreadShell,
|
|
7
|
+
} from "@t3tools/contracts";
|
|
8
|
+
import * as Schema from "effect/Schema";
|
|
15
9
|
|
|
16
|
-
export function isThreadActive(thread:
|
|
10
|
+
export function isThreadActive(thread: OrchestrationThreadShell | OrchestrationThread) {
|
|
17
11
|
return (
|
|
18
12
|
thread.session?.status === "starting" ||
|
|
19
13
|
thread.session?.status === "running" ||
|
|
@@ -22,18 +16,18 @@ export function isThreadActive(thread: ThreadShell | ThreadDetail) {
|
|
|
22
16
|
);
|
|
23
17
|
}
|
|
24
18
|
|
|
25
|
-
export function threadStatus(thread:
|
|
19
|
+
export function threadStatus(thread: OrchestrationThreadShell | OrchestrationThread) {
|
|
26
20
|
if (isPendingStart(thread)) {
|
|
27
21
|
return "pending";
|
|
28
22
|
}
|
|
29
23
|
return thread.session?.status ?? thread.latestTurn?.state ?? "unknown";
|
|
30
24
|
}
|
|
31
25
|
|
|
32
|
-
export function latestAssistantMessage(thread:
|
|
26
|
+
export function latestAssistantMessage(thread: OrchestrationThread) {
|
|
33
27
|
return thread.messages.toReversed().find((message) => message.role === "assistant");
|
|
34
28
|
}
|
|
35
29
|
|
|
36
|
-
export function isThreadCompleteEnough(thread:
|
|
30
|
+
export function isThreadCompleteEnough(thread: OrchestrationThread) {
|
|
37
31
|
if (thread.session?.status === "error" || thread.session?.status === "interrupted") {
|
|
38
32
|
return true;
|
|
39
33
|
}
|
|
@@ -45,16 +39,16 @@ export function isThreadCompleteEnough(thread: ThreadDetail) {
|
|
|
45
39
|
}
|
|
46
40
|
|
|
47
41
|
export function applyThreadEvent(
|
|
48
|
-
current:
|
|
49
|
-
event:
|
|
50
|
-
messages: Map<string,
|
|
42
|
+
current: OrchestrationThread,
|
|
43
|
+
event: OrchestrationEvent,
|
|
44
|
+
messages: Map<string, OrchestrationMessageType>,
|
|
51
45
|
) {
|
|
52
46
|
const message = messageFromEvent(event, messages);
|
|
53
47
|
if (message !== null) {
|
|
54
48
|
messages.set(messageKey(message), message);
|
|
55
49
|
return { ...current, messages: [...messages.values()] };
|
|
56
50
|
}
|
|
57
|
-
if (
|
|
51
|
+
if (event.type === "thread.session-set") {
|
|
58
52
|
return {
|
|
59
53
|
...current,
|
|
60
54
|
session: event.payload.session,
|
|
@@ -64,32 +58,32 @@ export function applyThreadEvent(
|
|
|
64
58
|
}
|
|
65
59
|
|
|
66
60
|
export function messageFromEvent(
|
|
67
|
-
event:
|
|
68
|
-
existingMessages: Map<string,
|
|
69
|
-
):
|
|
70
|
-
if (
|
|
61
|
+
event: OrchestrationEvent,
|
|
62
|
+
existingMessages: Map<string, OrchestrationMessageType> = new Map(),
|
|
63
|
+
): OrchestrationMessageType | null {
|
|
64
|
+
if (event.type !== "thread.message-sent") {
|
|
71
65
|
return null;
|
|
72
66
|
}
|
|
73
67
|
const payload = event.payload;
|
|
74
68
|
const id = payload.messageId;
|
|
75
69
|
const previous = existingMessages.get(id);
|
|
76
70
|
const text = payload.text;
|
|
77
|
-
return Schema.decodeUnknownSync(
|
|
71
|
+
return Schema.decodeUnknownSync(OrchestrationMessage)({
|
|
78
72
|
id,
|
|
79
73
|
role: payload.role,
|
|
80
74
|
text: text.length > 0 || previous === undefined ? text : previous.text,
|
|
81
75
|
turnId: payload.turnId,
|
|
82
|
-
|
|
76
|
+
streaming: payload.streaming,
|
|
83
77
|
createdAt: payload.createdAt,
|
|
84
78
|
updatedAt: payload.updatedAt,
|
|
85
79
|
});
|
|
86
80
|
}
|
|
87
81
|
|
|
88
|
-
export function messageKey(message:
|
|
89
|
-
return message.id
|
|
82
|
+
export function messageKey(message: OrchestrationMessageType) {
|
|
83
|
+
return message.id;
|
|
90
84
|
}
|
|
91
85
|
|
|
92
|
-
function isPendingStart(thread:
|
|
86
|
+
function isPendingStart(thread: OrchestrationThreadShell | OrchestrationThread) {
|
|
93
87
|
if (thread.session !== null || thread.latestTurn !== null || !("messages" in thread)) {
|
|
94
88
|
return false;
|
|
95
89
|
}
|
package/src/index.ts
CHANGED
|
@@ -7,12 +7,12 @@ export type {
|
|
|
7
7
|
} from "./application/service.ts";
|
|
8
8
|
export type { ApplicationError } from "./application/error.ts";
|
|
9
9
|
export type {
|
|
10
|
-
|
|
10
|
+
OrchestrationMessage,
|
|
11
|
+
OrchestrationProjectShell,
|
|
12
|
+
OrchestrationShellSnapshot,
|
|
13
|
+
OrchestrationThread,
|
|
14
|
+
OrchestrationThreadShell,
|
|
11
15
|
ServerProvider,
|
|
12
|
-
|
|
13
|
-
ThreadDetail,
|
|
14
|
-
ThreadMessage,
|
|
15
|
-
ThreadShell,
|
|
16
|
-
} from "./domain/schema.ts";
|
|
16
|
+
} from "@t3tools/contracts";
|
|
17
17
|
export { NodeEnvironmentLive } from "./environment/layer.ts";
|
|
18
18
|
export { AppLayer, AuthAppLayer } from "./runtime.ts";
|