@xfey/tutti 0.1.45 → 0.1.46
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 +1 -0
- package/dist/collaboration-state/index.d.ts +1 -1
- package/dist/collaboration-state/index.js +1 -1
- package/dist/collaboration-state/messages.d.ts +10 -1
- package/dist/collaboration-state/messages.js +48 -9
- package/dist/server-shell/cli/host-server-runtime.js +2 -0
- package/package.json +1 -1
- package/web/assets/index-BqiHeYIv.js +69 -0
- package/web/index.html +1 -1
- package/web/assets/index-B_3ILIDb.js +0 -69
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
## 当前状态
|
|
27
27
|
|
|
28
28
|
- 已具备 Fastify host shell、后台 host manager CLI 套件、带 `service status / stop / restart`、卸载前 `shutdown`、并发启动锁、版本替换、独立 browser context、idle / package update cleanup 与私有日志的机器级 Local Console、Dev Runner、Host Project API、SSE、SQLite store、collaboration-state、workspace read-state、project timeline observability helper、Control Plane、Procedure / Run、workspace-ops、checks、Relay staged upload finalize、user skills 文件服务、artifacts 可视化产物查看层和 OpenAI / Codex provider 接入。
|
|
29
|
+
- 新 Host project 的空群聊会由 collaboration-state 幂等写入一条 Tutti 欢迎消息;它不经过 browser message command,不触发 `@tutti`、Chat read-state、Scratchpad 或 Timeline / execution 流程。
|
|
29
30
|
- Host server 与 Local Console 支持在显式 `webStatic.root` 或 packaged `web/` 存在时服务 Web 静态产物;`/api/*`、SSE、health、host-local control 和 `/local-console/*` API 不被 SPA fallback 吞掉。
|
|
30
31
|
- Root `pack:dry-run` 会先清理 build output、重新构建,再复制 `apps/web/dist` 到 package-local `web/`,生成临时 staging `@xfey/tutti` artifact,并校验 npm pack 文件列表包含 CLI、migrations、prompts、Web index、bundled `@tutti/shared` / `@tutti/relay-client` 以及它们的外部 runtime dependencies,同时排除源码、source map、日志、SQLite、`.env`、`.tutti-dev` 与测试产物。
|
|
31
32
|
- Root `smoke:packed-cli` 同样从干净 build output 生成 staging `@xfey/tutti` tarball、在临时目录安装,并验证 SQLite native binding、`tutti --help` / `tutti service --help` / `tutti service status` / `tutti shutdown --help` / `tutti launch --help` / `tutti archive --help` / `tutti --version`、migrations、prompts、Web static index、bundled internal dependencies、bundled external runtime dependencies、CLI QR dependency、仓库锁定的 `@openai/codex` binary 可执行性和临时全局安装路径;该 smoke 不需要 Tutti source repo 存在。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { CollaborationStateCursorError } from "./serialization.js";
|
|
2
2
|
export { createIdleExecutionStatusProjection } from "./execution-status.js";
|
|
3
|
-
export { createMainChatAgentMessage, createMainChatMessage, createMainChatSystemMessage, hasMainChatArtifactPreviewMessage, hasMainChatTaskResultMessage, hasMainChatWorklistCompileMessage, hasMainChatWorklistFeedbackMessage, readCompletedWorklistFeedbackTaskIds, readMessageProjectionById, readMainChatMessageWindow, readMainChatMessagesAfter, readMainChatMessagesAfterCursor, readMainChatMessagesBeforeCursor, } from "./messages.js";
|
|
3
|
+
export { createMainChatAgentMessage, createMainChatMessage, createMainChatSystemMessage, ensureInitialProjectWelcomeMessage, hasMainChatArtifactPreviewMessage, hasMainChatTaskResultMessage, hasMainChatWorklistCompileMessage, hasMainChatWorklistFeedbackMessage, readCompletedWorklistFeedbackTaskIds, readMessageProjectionById, readMainChatMessageWindow, readMainChatMessagesAfter, readMainChatMessagesAfterCursor, readMainChatMessagesBeforeCursor, } from "./messages.js";
|
|
4
4
|
export { createEmptyScratchpadProjection, readScratchpadReceiptSourceRefs, readScratchpadProjection, submitScratchpadProjection, upsertScratchpadProjection, } from "./scratchpad.js";
|
|
5
5
|
export { advanceScratchpadTaskCompileSourceCursor, markScratchpadRefreshFailed, markScratchpadRefreshStarted, markScratchpadRefreshSucceeded, markScratchpadSourceDirty, readScratchpadSourceState, } from "./scratchpad-source-state.js";
|
|
6
6
|
export { deleteStaleClarificationSubmitReplayHints, readActiveClarificationProjection, readClarificationRoundProjection, readClarificationRoundsForThread, readClarificationThreadResponse, readSealedClarificationRoundsWithoutSuccessor, readSubmittedTaskCompileClarificationRounds, readSubmittedTaskCompileClarificationRoundsAfterCursor, } from "./clarification-records.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { CollaborationStateCursorError } from "./serialization.js";
|
|
2
2
|
export { createIdleExecutionStatusProjection } from "./execution-status.js";
|
|
3
|
-
export { createMainChatAgentMessage, createMainChatMessage, createMainChatSystemMessage, hasMainChatArtifactPreviewMessage, hasMainChatTaskResultMessage, hasMainChatWorklistCompileMessage, hasMainChatWorklistFeedbackMessage, readCompletedWorklistFeedbackTaskIds, readMessageProjectionById, readMainChatMessageWindow, readMainChatMessagesAfter, readMainChatMessagesAfterCursor, readMainChatMessagesBeforeCursor, } from "./messages.js";
|
|
3
|
+
export { createMainChatAgentMessage, createMainChatMessage, createMainChatSystemMessage, ensureInitialProjectWelcomeMessage, hasMainChatArtifactPreviewMessage, hasMainChatTaskResultMessage, hasMainChatWorklistCompileMessage, hasMainChatWorklistFeedbackMessage, readCompletedWorklistFeedbackTaskIds, readMessageProjectionById, readMainChatMessageWindow, readMainChatMessagesAfter, readMainChatMessagesAfterCursor, readMainChatMessagesBeforeCursor, } from "./messages.js";
|
|
4
4
|
export { createEmptyScratchpadProjection, readScratchpadReceiptSourceRefs, readScratchpadProjection, submitScratchpadProjection, upsertScratchpadProjection, } from "./scratchpad.js";
|
|
5
5
|
export { advanceScratchpadTaskCompileSourceCursor, markScratchpadRefreshFailed, markScratchpadRefreshStarted, markScratchpadRefreshSucceeded, markScratchpadSourceDirty, readScratchpadSourceState, } from "./scratchpad-source-state.js";
|
|
6
6
|
export { deleteStaleClarificationSubmitReplayHints, readActiveClarificationProjection, readClarificationRoundProjection, readClarificationRoundsForThread, readClarificationThreadResponse, readSealedClarificationRoundsWithoutSuccessor, readSubmittedTaskCompileClarificationRounds, readSubmittedTaskCompileClarificationRoundsAfterCursor, } from "./clarification-records.js";
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { type MessageId, type RunResultId, type TaskId, type WorkflowInvocationRef } from "@tutti/shared/ids";
|
|
2
2
|
import type { MessageProjection, MessageWindow } from "@tutti/shared/schemas/api";
|
|
3
|
-
import type
|
|
3
|
+
import { type SqliteDatabase } from "../store/index.js";
|
|
4
4
|
import type { CreateMainChatAgentMessageInput, CreateMainChatMessageInput, CreateMainChatSystemMessageInput, ReadMainChatMessagesAfterOptions, ReadMessageWindowOptions } from "./types.js";
|
|
5
5
|
import type { MessageRow } from "./storage-types.js";
|
|
6
6
|
export declare function mapMessageRow(row: MessageRow, db?: SqliteDatabase): MessageProjection;
|
|
7
7
|
export declare function createMainChatMessage(db: SqliteDatabase, input: CreateMainChatMessageInput): MessageProjection;
|
|
8
8
|
export declare function createMainChatSystemMessage(db: SqliteDatabase, input: CreateMainChatSystemMessageInput): MessageProjection;
|
|
9
9
|
export declare function createMainChatAgentMessage(db: SqliteDatabase, input: CreateMainChatAgentMessageInput): MessageProjection;
|
|
10
|
+
/**
|
|
11
|
+
* Seeds onboarding copy only while a project chat is still empty. The direct
|
|
12
|
+
* repository write is intentional: this is not user discussion, so it must not
|
|
13
|
+
* trigger the chat assistant, read-state notifications, or Scratchpad work.
|
|
14
|
+
*/
|
|
15
|
+
export declare function ensureInitialProjectWelcomeMessage(db: SqliteDatabase, input: {
|
|
16
|
+
projectName: string;
|
|
17
|
+
now?: () => Date;
|
|
18
|
+
}): MessageProjection | null;
|
|
10
19
|
export declare function readMessageProjectionById(db: SqliteDatabase, messageId: MessageId): MessageProjection | null;
|
|
11
20
|
export declare function hasMainChatWorklistFeedbackMessage(db: SqliteDatabase, feedbackKey: string): boolean;
|
|
12
21
|
export declare function hasMainChatTaskResultMessage(db: SqliteDatabase, resultKey: string): boolean;
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { createMessageId, } from "@tutti/shared/ids";
|
|
2
|
+
import { withHostStoreTransaction } from "../store/index.js";
|
|
2
3
|
import { clampLimit, decodeCursor, messageCursor, nowIso, parseJson, stringifyJson, } from "./serialization.js";
|
|
3
4
|
import { recordReadNotificationEvent } from "./read-state.js";
|
|
5
|
+
function escapeMarkdownText(value) {
|
|
6
|
+
return value.replace(/[!-/:-@[-`{-~]/gu, "\\$&");
|
|
7
|
+
}
|
|
8
|
+
function initialProjectWelcomeMessage(projectName) {
|
|
9
|
+
const normalizedProjectName = projectName.trim().replace(/\s+/gu, " ") || "this project";
|
|
10
|
+
return [
|
|
11
|
+
`Welcome to ${escapeMarkdownText(normalizedProjectName)} 👋`,
|
|
12
|
+
"This is where your team can shape ideas, align on goals, and bring the project to life. Tutti will keep the Scratchpad updated as the conversation develops.",
|
|
13
|
+
"When you’re ready, click Run to begin implementation. You can also mention `@tutti` whenever you need help.",
|
|
14
|
+
].join("\n\n");
|
|
15
|
+
}
|
|
4
16
|
function readClarificationCardForMessage(db, messageId) {
|
|
5
17
|
const row = db
|
|
6
18
|
.prepare(`
|
|
@@ -148,7 +160,7 @@ export function createMainChatSystemMessage(db, input) {
|
|
|
148
160
|
}
|
|
149
161
|
return message;
|
|
150
162
|
}
|
|
151
|
-
|
|
163
|
+
function insertMainChatAgentMessage(db, input, options) {
|
|
152
164
|
const createdAt = nowIso(input.now);
|
|
153
165
|
const id = createMessageId();
|
|
154
166
|
db.prepare(`
|
|
@@ -165,13 +177,15 @@ export function createMainChatAgentMessage(db, input) {
|
|
|
165
177
|
)
|
|
166
178
|
VALUES (?, 'main_chat', 'agent', 'tutti', 'Tutti', 'agent_text', ?, ?, ?)
|
|
167
179
|
`).run(id, input.body, input.refs === undefined ? null : stringifyJson(input.refs), createdAt);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
180
|
+
if (options.recordReadState) {
|
|
181
|
+
recordReadNotificationEvent(db, {
|
|
182
|
+
scope: "chat",
|
|
183
|
+
source_kind: "message",
|
|
184
|
+
source_ref: id,
|
|
185
|
+
actor: { kind: "agent", ref: "tutti" },
|
|
186
|
+
occurred_at: createdAt,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
175
189
|
const message = {
|
|
176
190
|
id,
|
|
177
191
|
scope: { kind: "main_chat" },
|
|
@@ -185,6 +199,27 @@ export function createMainChatAgentMessage(db, input) {
|
|
|
185
199
|
}
|
|
186
200
|
return message;
|
|
187
201
|
}
|
|
202
|
+
export function createMainChatAgentMessage(db, input) {
|
|
203
|
+
return insertMainChatAgentMessage(db, input, { recordReadState: true });
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Seeds onboarding copy only while a project chat is still empty. The direct
|
|
207
|
+
* repository write is intentional: this is not user discussion, so it must not
|
|
208
|
+
* trigger the chat assistant, read-state notifications, or Scratchpad work.
|
|
209
|
+
*/
|
|
210
|
+
export function ensureInitialProjectWelcomeMessage(db, input) {
|
|
211
|
+
return withHostStoreTransaction(db, (tx) => {
|
|
212
|
+
const existing = tx.prepare("SELECT 1 AS found FROM messages LIMIT 1").get();
|
|
213
|
+
if (existing !== undefined) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
return insertMainChatAgentMessage(tx, {
|
|
217
|
+
body: initialProjectWelcomeMessage(input.projectName),
|
|
218
|
+
refs: { scratchpad_source: "exclude" },
|
|
219
|
+
...(input.now === undefined ? {} : { now: input.now }),
|
|
220
|
+
}, { recordReadState: false });
|
|
221
|
+
});
|
|
222
|
+
}
|
|
188
223
|
export function readMessageProjectionById(db, messageId) {
|
|
189
224
|
const row = db
|
|
190
225
|
.prepare(`
|
|
@@ -330,7 +365,11 @@ export function readMainChatMessageWindow(db, options = {}) {
|
|
|
330
365
|
.all(target.created_at, target.created_at, target.id, afterLimit + 1);
|
|
331
366
|
hasOlder = olderRows.length > beforeLimit;
|
|
332
367
|
hasNewer = newerRows.length > afterLimit;
|
|
333
|
-
rows = [
|
|
368
|
+
rows = [
|
|
369
|
+
...olderRows.slice(0, beforeLimit).reverse(),
|
|
370
|
+
target,
|
|
371
|
+
...newerRows.slice(0, afterLimit),
|
|
372
|
+
];
|
|
334
373
|
}
|
|
335
374
|
}
|
|
336
375
|
else if (before !== null) {
|
|
@@ -3,6 +3,7 @@ import { serializeRuntimeLogLine } from "@tutti/shared/utils";
|
|
|
3
3
|
import { completeRelayUpload, resolveRelayUpload } from "@tutti/relay-client";
|
|
4
4
|
import { ApprovalRequestManager } from "../../approvals/index.js";
|
|
5
5
|
import { ReadOnlyChatAssistant } from "../../chat-assistant/index.js";
|
|
6
|
+
import { ensureInitialProjectWelcomeMessage } from "../../collaboration-state/index.js";
|
|
6
7
|
import { resolveControlPlaneCommandRecovery } from "../../control-plane/command-recovery.js";
|
|
7
8
|
import { Phase5ControlPlane } from "../../control-plane/index.js";
|
|
8
9
|
import { resolveProjectOpenAiProcedureWorkflowRunner } from "../../control-plane/workflows/index.js";
|
|
@@ -156,6 +157,7 @@ export async function startForegroundHostServer(options) {
|
|
|
156
157
|
};
|
|
157
158
|
let projectDisplayName = readProjectDisplayName(options.preparation.workspace_root) ??
|
|
158
159
|
(basename(options.preparation.workspace_root) || options.preparation.project_id);
|
|
160
|
+
ensureInitialProjectWelcomeMessage(store.db, { projectName: projectDisplayName, now });
|
|
159
161
|
let projectDescription = readProjectDescription(options.preparation.workspace_root) ?? "";
|
|
160
162
|
const projectContext = {
|
|
161
163
|
workspaceRoot: options.preparation.workspace_root,
|