@xfey/tutti 0.1.103 → 0.1.104

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.
Files changed (106) hide show
  1. package/README.md +9 -4
  2. package/dist/chat-assistant/index.d.ts +6 -1
  3. package/dist/chat-assistant/index.js +22 -12
  4. package/dist/collaboration-ingestion/external-source.d.ts +15 -0
  5. package/dist/collaboration-ingestion/external-source.js +120 -0
  6. package/dist/collaboration-ingestion/index.d.ts +5 -0
  7. package/dist/collaboration-ingestion/index.js +4 -0
  8. package/dist/collaboration-ingestion/invalidations.d.ts +4 -0
  9. package/dist/collaboration-ingestion/invalidations.js +15 -0
  10. package/dist/collaboration-ingestion/main-chat.d.ts +33 -0
  11. package/dist/collaboration-ingestion/main-chat.js +67 -0
  12. package/dist/collaboration-ingestion/reference-files.d.ts +29 -0
  13. package/dist/collaboration-ingestion/reference-files.js +158 -0
  14. package/dist/collaboration-ingestion/types.d.ts +19 -0
  15. package/dist/collaboration-ingestion/types.js +2 -0
  16. package/dist/platform-outbound/coordinator.d.ts +17 -0
  17. package/dist/platform-outbound/coordinator.js +48 -0
  18. package/dist/platform-outbound/index.d.ts +5 -0
  19. package/dist/platform-outbound/index.js +5 -0
  20. package/dist/platform-outbound/repository.d.ts +30 -0
  21. package/dist/platform-outbound/repository.js +108 -0
  22. package/dist/platform-outbound/types.d.ts +38 -0
  23. package/dist/platform-outbound/types.js +2 -0
  24. package/dist/platform-outbound/worker.d.ts +36 -0
  25. package/dist/platform-outbound/worker.js +164 -0
  26. package/dist/server-shell/cli/cli.js +9 -0
  27. package/dist/server-shell/cli/errors.d.ts +1 -1
  28. package/dist/server-shell/cli/host-lifecycle.d.ts +2 -0
  29. package/dist/server-shell/cli/host-lifecycle.js +34 -1
  30. package/dist/server-shell/cli/host-relay-connection-manager.js +22 -11
  31. package/dist/server-shell/cli/host-relay-status.js +1 -0
  32. package/dist/server-shell/cli/host-runtime-endpoint.js +17 -1
  33. package/dist/server-shell/cli/host-server-runtime.d.ts +2 -0
  34. package/dist/server-shell/cli/host-server-runtime.js +88 -14
  35. package/dist/server-shell/cli/launch-command.d.ts +7 -0
  36. package/dist/server-shell/cli/launch-command.js +39 -1
  37. package/dist/server-shell/cli/launch.d.ts +2 -0
  38. package/dist/server-shell/cli/launch.js +3 -0
  39. package/dist/server-shell/cli/machine-local.d.ts +20 -0
  40. package/dist/server-shell/cli/machine-local.js +61 -1
  41. package/dist/server-shell/cli/managed-host.d.ts +16 -1
  42. package/dist/server-shell/cli/managed-host.js +191 -23
  43. package/dist/server-shell/cli/relay-registration.d.ts +4 -1
  44. package/dist/server-shell/cli/relay-registration.js +36 -7
  45. package/dist/server-shell/cli/runtime-commands.d.ts +5 -1
  46. package/dist/server-shell/cli/runtime-commands.js +9 -3
  47. package/dist/server-shell/http/host-tunnel.d.ts +5 -0
  48. package/dist/server-shell/http/host-tunnel.js +109 -0
  49. package/dist/server-shell/http/routes/local-control.d.ts +2 -0
  50. package/dist/server-shell/http/routes/local-control.js +2 -0
  51. package/dist/server-shell/http/routes/project-api/invalidations.d.ts +0 -1
  52. package/dist/server-shell/http/routes/project-api/invalidations.js +0 -3
  53. package/dist/server-shell/http/routes/project-api/messages-routes.js +32 -63
  54. package/dist/server-shell/http/routes/project-api/openapi-workspace-routes.d.ts +8 -4
  55. package/dist/server-shell/http/routes/project-api/openapi.d.ts +8 -4
  56. package/dist/server-shell/http/routes/project-api/reference-files-routes.js +25 -94
  57. package/dist/server-shell/http/routes/project-api/reference-files.d.ts +1 -11
  58. package/dist/server-shell/http/routes/project-api/reference-files.js +2 -87
  59. package/dist/server-shell/http/routes/project-api/schemas.d.ts +8 -4
  60. package/dist/server-shell/http/routes/project-api/schemas.js +8 -3
  61. package/dist/server-shell/http/routes/project-api/session-requirements.js +34 -2
  62. package/dist/server-shell/http/routes/project-api/types.d.ts +3 -1
  63. package/dist/server-shell/http/routes/project-api/workspace-projections.js +8 -2
  64. package/dist/server-shell/local-console/package-update-hosts.js +4 -1
  65. package/dist/server-shell/local-console/project-service.d.ts +11 -2
  66. package/dist/server-shell/local-console/project-service.js +36 -8
  67. package/dist/server-shell/local-console/server.js +6 -1
  68. package/dist/server-shell/local-console/session.d.ts +4 -0
  69. package/dist/server-shell/local-console/session.js +14 -0
  70. package/dist/server-shell/session/relay-session-context.d.ts +12 -1
  71. package/dist/server-shell/session/relay-session-context.js +19 -1
  72. package/migrations/0016_external_source_mappings.sql +141 -0
  73. package/migrations/0017_platform_outbound_intents.sql +31 -0
  74. package/migrations/README.md +3 -1
  75. package/node_modules/@tutti/relay-client/dist/host-control.d.ts +26 -1
  76. package/node_modules/@tutti/relay-client/dist/host-control.js +62 -0
  77. package/node_modules/@tutti/relay-client/dist/tunnel-frames.js +32 -6
  78. package/node_modules/@tutti/relay-client/dist/tunnel-types.d.ts +5 -1
  79. package/node_modules/@tutti/shared/dist/domain/index.d.ts +9 -0
  80. package/node_modules/@tutti/shared/dist/domain/index.js +14 -0
  81. package/node_modules/@tutti/shared/dist/ids/index.d.ts +6 -0
  82. package/node_modules/@tutti/shared/dist/ids/index.js +4 -0
  83. package/node_modules/@tutti/shared/dist/schemas/api/index.d.ts +4 -0
  84. package/node_modules/@tutti/shared/dist/schemas/api/index.js +2 -0
  85. package/node_modules/@tutti/shared/dist/schemas/api/local-console-account-handoffs.d.ts +13 -0
  86. package/node_modules/@tutti/shared/dist/schemas/api/local-console-account-handoffs.js +16 -0
  87. package/node_modules/@tutti/shared/dist/schemas/api/platform-sessions.d.ts +43 -0
  88. package/node_modules/@tutti/shared/dist/schemas/api/platform-sessions.js +44 -0
  89. package/node_modules/@tutti/shared/dist/schemas/domain/index.d.ts +7 -0
  90. package/node_modules/@tutti/shared/dist/schemas/domain/index.js +10 -0
  91. package/node_modules/@tutti/shared/dist/schemas/internal/index.d.ts +29 -0
  92. package/node_modules/@tutti/shared/dist/schemas/internal/index.js +24 -1
  93. package/node_modules/@tutti/shared/dist/schemas/internal/platform-integration.d.ts +1346 -0
  94. package/node_modules/@tutti/shared/dist/schemas/internal/platform-integration.js +516 -0
  95. package/node_modules/@tutti/shared/dist/utils/feishu-diagnostics/index.d.ts +10 -0
  96. package/node_modules/@tutti/shared/dist/utils/feishu-diagnostics/index.js +19 -0
  97. package/node_modules/@tutti/shared/dist/utils/index.d.ts +1 -0
  98. package/node_modules/@tutti/shared/dist/utils/index.js +1 -0
  99. package/node_modules/@tutti/shared/dist/utils/redaction/index.d.ts +1 -1
  100. package/node_modules/@tutti/shared/dist/utils/redaction/index.js +18 -0
  101. package/package.json +1 -1
  102. package/web/assets/{homepage-motion-scene-CPs4EYQq.js → homepage-motion-scene-DEVrQ9aj.js} +1 -1
  103. package/web/assets/{index-CxCTqGFO.css → index-6i9Uk7fS.css} +1 -1
  104. package/web/assets/index-Lxtst2sl.js +69 -0
  105. package/web/index.html +2 -2
  106. package/web/assets/index-D3lwP_UR.js +0 -69
@@ -1,9 +1,8 @@
1
1
  import { GetMessagesRequestSchema, MessageWindowSchema, SendMainChatMessageBodySchema, SendMainChatMessageResponseSchema, } from "@tutti/shared/schemas/api";
2
- import { createMainChatMessage, readMessageProjectionById, readMainChatMessageWindow, } from "../../../../collaboration-state/index.js";
3
- import { createArtifactFeedbackMessage, } from "../../../../artifact-feedback/index.js";
4
- import { executeIdempotentCommand } from "../../../command-idempotency/index.js";
2
+ import { createArtifactFeedbackMessage } from "../../../../artifact-feedback/index.js";
3
+ import { ingestMainChatMessage } from "../../../../collaboration-ingestion/index.js";
4
+ import { readMainChatMessageWindow } from "../../../../collaboration-state/index.js";
5
5
  import { HOST_PROJECT_API_BASE_PATH } from "./constants.js";
6
- import { mainChatMessageInvalidates } from "./invalidations.js";
7
6
  import { requireValidMessageWindowRequest, requireValidSendMainChatPayload, } from "./payload-validation.js";
8
7
  import { requireHostProjectSession, requireProjectStore, requireWorkspaceRoot, } from "./session-requirements.js";
9
8
  export function registerMessagesRoutes(app, { options, workspaceEvents, artifactPreviewManager, }) {
@@ -30,83 +29,53 @@ export function registerMessagesRoutes(app, { options, workspaceEvents, artifact
30
29
  const session = requireHostProjectSession(request, options);
31
30
  const store = requireProjectStore(options);
32
31
  const payload = requireValidSendMainChatPayload(request.body.payload);
33
- const execution = executeIdempotentCommand({
34
- db: store.db,
35
- command_id: request.body.command_id,
36
- command_name: "messages.create",
37
- target_ref: "main_chat",
38
- payload: { ...payload, author_ref: session.account.account_ref },
32
+ return ingestMainChatMessage({
33
+ store,
34
+ events: workspaceEvents,
35
+ commandId: request.body.command_id,
36
+ payload,
37
+ author: session.account,
39
38
  ...(options.now === undefined ? {} : { now: options.now }),
40
- execute: (tx) => {
41
- let body;
42
- let refs;
39
+ materialize: () => {
43
40
  if ("artifact_feedback" in payload) {
44
41
  const projection = artifactPreviewManager.readProjection({
45
42
  workspaceRoot: requireWorkspaceRoot(options),
46
43
  });
47
44
  if (projection.artifact.status !== "declared" ||
48
45
  projection.artifact.preview.status !== "ready") {
49
- return { disposition: { kind: "artifact_unavailable" } };
46
+ return {
47
+ kind: "rejected",
48
+ disposition: { kind: "artifact_unavailable" },
49
+ };
50
50
  }
51
51
  if (projection.artifact.preview_generation !==
52
52
  payload.artifact_feedback.expected_preview_generation) {
53
- return { disposition: { kind: "artifact_changed" } };
53
+ return {
54
+ kind: "rejected",
55
+ disposition: { kind: "artifact_changed" },
56
+ };
54
57
  }
55
58
  const feedbackMessage = createArtifactFeedbackMessage({
56
59
  artifactTitle: projection.artifact.artifact.title,
57
60
  feedback: payload.artifact_feedback,
58
61
  });
59
- body = feedbackMessage.body;
60
- refs = { artifact_feedback: feedbackMessage.ref };
62
+ return {
63
+ kind: "message",
64
+ body: feedbackMessage.body,
65
+ refs: { artifact_feedback: feedbackMessage.ref },
66
+ };
61
67
  }
62
- else {
63
- body = payload.body;
64
- }
65
- const message = createMainChatMessage(tx, {
66
- body,
67
- author: {
68
- kind: "human",
69
- ref: session.account.account_ref,
70
- display_name: session.account.display_name,
71
- avatar: session.account.avatar,
72
- },
73
- ...(refs === undefined ? {} : { refs }),
74
- ...(options.now === undefined ? {} : { now: options.now }),
75
- });
76
- return {
77
- disposition: { kind: "created", message_id: message.id },
78
- result: { message },
79
- };
68
+ return { kind: "message", body: payload.body };
80
69
  },
70
+ ...(options.controlPlane === undefined
71
+ ? {}
72
+ : {
73
+ scratchpadSource: {
74
+ notify: (message) => options.controlPlane?.notifyScratchpadSourceChanged(message),
75
+ },
76
+ }),
77
+ ...(options.chatAssistant === undefined ? {} : { chatAssistant: options.chatAssistant }),
81
78
  });
82
- const replayedMessage = execution.result === undefined &&
83
- execution.disposition.kind === "created" &&
84
- execution.disposition.message_id !== undefined
85
- ? readMessageProjectionById(store.db, execution.disposition.message_id)
86
- : null;
87
- const result = execution.result ?? (replayedMessage === null ? undefined : { message: replayedMessage });
88
- const response = {
89
- command_id: execution.command_id,
90
- disposition: execution.disposition,
91
- ...(execution.disposition.kind === "created"
92
- ? { invalidates: mainChatMessageInvalidates() }
93
- : {}),
94
- };
95
- if (result !== undefined) {
96
- response.result = result;
97
- }
98
- if (!execution.replayed && result !== undefined) {
99
- workspaceEvents.publish({
100
- event_type: "message.created",
101
- payload: {
102
- message: result.message,
103
- },
104
- invalidates: mainChatMessageInvalidates(),
105
- });
106
- options.controlPlane?.notifyScratchpadSourceChanged(result.message);
107
- options.chatAssistant?.enqueue({ message: result.message });
108
- }
109
- return response;
110
79
  });
111
80
  }
112
81
  //# sourceMappingURL=messages-routes.js.map
@@ -429,10 +429,12 @@ export declare const HOST_PROJECT_WORKSPACE_OPENAPI_ROUTES: ({
429
429
  readonly required: readonly ["client_session_id", "transport", "connected_at"];
430
430
  readonly additionalProperties: false;
431
431
  readonly properties: {
432
- readonly client_session_id: import("@sinclair/typebox").TString;
432
+ readonly client_session_id: {
433
+ readonly oneOf: readonly [import("@sinclair/typebox").TString, import("@sinclair/typebox").TString];
434
+ };
433
435
  readonly transport: {
434
436
  readonly type: "string";
435
- readonly enum: readonly ["relay_tunnel"];
437
+ readonly enum: readonly ["relay_tunnel", "feishu_platform_session"];
436
438
  };
437
439
  readonly connected_at: import("@sinclair/typebox").TString;
438
440
  };
@@ -895,10 +897,12 @@ export declare const HOST_PROJECT_WORKSPACE_OPENAPI_ROUTES: ({
895
897
  readonly required: readonly ["client_session_id", "transport", "connected_at"];
896
898
  readonly additionalProperties: false;
897
899
  readonly properties: {
898
- readonly client_session_id: import("@sinclair/typebox").TString;
900
+ readonly client_session_id: {
901
+ readonly oneOf: readonly [import("@sinclair/typebox").TString, import("@sinclair/typebox").TString];
902
+ };
899
903
  readonly transport: {
900
904
  readonly type: "string";
901
- readonly enum: readonly ["relay_tunnel"];
905
+ readonly enum: readonly ["relay_tunnel", "feishu_platform_session"];
902
906
  };
903
907
  readonly connected_at: import("@sinclair/typebox").TString;
904
908
  };
@@ -1712,10 +1712,12 @@ export declare const HOST_PROJECT_OPENAPI_ROUTES: ({
1712
1712
  readonly required: readonly ["client_session_id", "transport", "connected_at"];
1713
1713
  readonly additionalProperties: false;
1714
1714
  readonly properties: {
1715
- readonly client_session_id: import("@sinclair/typebox").TString;
1715
+ readonly client_session_id: {
1716
+ readonly oneOf: readonly [import("@sinclair/typebox").TString, import("@sinclair/typebox").TString];
1717
+ };
1716
1718
  readonly transport: {
1717
1719
  readonly type: "string";
1718
- readonly enum: readonly ["relay_tunnel"];
1720
+ readonly enum: readonly ["relay_tunnel", "feishu_platform_session"];
1719
1721
  };
1720
1722
  readonly connected_at: import("@sinclair/typebox").TString;
1721
1723
  };
@@ -2178,10 +2180,12 @@ export declare const HOST_PROJECT_OPENAPI_ROUTES: ({
2178
2180
  readonly required: readonly ["client_session_id", "transport", "connected_at"];
2179
2181
  readonly additionalProperties: false;
2180
2182
  readonly properties: {
2181
- readonly client_session_id: import("@sinclair/typebox").TString;
2183
+ readonly client_session_id: {
2184
+ readonly oneOf: readonly [import("@sinclair/typebox").TString, import("@sinclair/typebox").TString];
2185
+ };
2182
2186
  readonly transport: {
2183
2187
  readonly type: "string";
2184
- readonly enum: readonly ["relay_tunnel"];
2188
+ readonly enum: readonly ["relay_tunnel", "feishu_platform_session"];
2185
2189
  };
2186
2190
  readonly connected_at: import("@sinclair/typebox").TString;
2187
2191
  };
@@ -1,9 +1,9 @@
1
1
  import { GetReferenceFilesRequestSchema, GetReferenceFilesResponseSchema, RefreshReferenceSummariesBodySchema, RefreshReferenceSummariesResponseSchema, UploadReferenceFileBodySchema, UploadReferenceFileResponseSchema, } from "@tutti/shared/schemas/api";
2
- import { createMainChatMessage, recordReadNotificationEvent, } from "../../../../collaboration-state/index.js";
3
- import { REFERENCE_DIRECTORY_PATH, uploadReferenceFile, } from "../../../../workspace-ops/index.js";
2
+ import { ingestReferenceFile, REFERENCE_FILE_INGESTION_COMMAND_NAME, REFERENCE_FILE_INGESTION_TARGET_REF, } from "../../../../collaboration-ingestion/index.js";
3
+ import { REFERENCE_DIRECTORY_PATH } from "../../../../workspace-ops/index.js";
4
4
  import { decideCommandIdempotencyReplay, executeIdempotentCommand, readCommandIdempotencyRecord, } from "../../../command-idempotency/index.js";
5
5
  import { HOST_PROJECT_API_BASE_PATH } from "./constants.js";
6
- import { readReferenceFilesResponse, referenceFileMessageRef, referenceFilesChangedInvalidates, referenceSummaryProcedureInvalidates, referenceUploadMessageBody, toReferenceFileProjection, } from "./reference-files.js";
6
+ import { readReferenceFilesResponse, referenceSummaryProcedureInvalidates, } from "./reference-files.js";
7
7
  import { requireControlPlane, requireHostProjectSession, requireProjectStore, requireWorkspaceRoot, } from "./session-requirements.js";
8
8
  import { abortDownloadedStagedUpload, cleanupDownloadedStagedUpload, downloadStagedUpload, } from "./staged-uploads.js";
9
9
  export function registerReferenceFilesRoutes(app, { options, workspaceEvents, }) {
@@ -33,8 +33,8 @@ export function registerReferenceFilesRoutes(app, { options, workspaceEvents, })
33
33
  const store = requireProjectStore(options);
34
34
  const commandId = request.body.command_id;
35
35
  const command = {
36
- command_name: "references.files.upload",
37
- target_ref: REFERENCE_DIRECTORY_PATH,
36
+ command_name: REFERENCE_FILE_INGESTION_COMMAND_NAME,
37
+ target_ref: REFERENCE_FILE_INGESTION_TARGET_REF,
38
38
  payload: request.body.payload,
39
39
  };
40
40
  const replay = decideCommandIdempotencyReplay(readCommandIdempotencyRecord(store.db, commandId), command);
@@ -48,101 +48,32 @@ export function registerReferenceFilesRoutes(app, { options, workspaceEvents, })
48
48
  purpose: "reference_file",
49
49
  })
50
50
  : undefined;
51
- const invalidates = referenceFilesChangedInvalidates();
52
- const execution = executeIdempotentCommand({
53
- db: store.db,
54
- command_id: commandId,
55
- command_name: command.command_name,
56
- target_ref: command.target_ref,
51
+ return await ingestReferenceFile({
52
+ store,
53
+ workspaceRoot: requireWorkspaceRoot(options),
54
+ events: workspaceEvents,
55
+ commandId,
57
56
  payload: request.body.payload,
57
+ content: staged?.content,
58
+ author: session.account,
58
59
  ...(options.now === undefined ? {} : { now: options.now }),
59
- execute: (tx) => {
60
- if (staged === undefined) {
61
- throw new Error("Staged reference upload was not downloaded");
60
+ afterCommit: async () => {
61
+ if (staged !== undefined) {
62
+ await cleanupDownloadedStagedUpload(staged);
63
+ staged = undefined;
62
64
  }
63
- const uploaded = uploadReferenceFile({
64
- workspaceRoot: requireWorkspaceRoot(options),
65
- commandId,
66
- fileName: request.body.payload.file_name,
67
- content: staged.content,
68
- ...(request.body.payload.media_type === undefined
69
- ? {}
70
- : { mediaType: request.body.payload.media_type }),
71
- ...(request.body.payload.note === undefined
72
- ? {}
73
- : { note: request.body.payload.note }),
74
- ...(options.now === undefined ? {} : { now: options.now }),
75
- });
76
- const file = toReferenceFileProjection({
77
- workspaceRoot: requireWorkspaceRoot(options),
78
- entry: uploaded.file,
79
- ...(options.now === undefined ? {} : { now: options.now }),
80
- });
81
- const message = createMainChatMessage(tx, {
82
- body: referenceUploadMessageBody(file),
83
- author: {
84
- kind: "human",
85
- ref: session.account.account_ref,
86
- display_name: session.account.display_name,
87
- avatar: session.account.avatar,
88
- },
89
- refs: {
90
- reference_file: referenceFileMessageRef(file),
91
- },
92
- ...(options.now === undefined ? {} : { now: options.now }),
93
- });
94
- recordReadNotificationEvent(tx, {
95
- scope: "references",
96
- source_kind: "reference_file",
97
- source_ref: file.path,
98
- actor: {
99
- kind: "human",
100
- ref: session.account.account_ref,
65
+ },
66
+ ...(options.controlPlane === undefined
67
+ ? {}
68
+ : {
69
+ scratchpadSource: {
70
+ notify: (message) => options.controlPlane?.notifyScratchpadSourceChanged(message),
101
71
  },
102
- occurred_at: message.created_at,
103
- });
104
- return {
105
- disposition: { kind: "created" },
106
- result: {
107
- file,
108
- message,
109
- index_path: uploaded.index_path,
72
+ referenceSummaries: {
73
+ start: (paths) => options.controlPlane?.startReferenceSummaryRefresh({ paths }),
110
74
  },
111
- };
112
- },
75
+ }),
113
76
  });
114
- const response = {
115
- command_id: execution.command_id,
116
- disposition: execution.disposition,
117
- invalidates,
118
- };
119
- if (execution.result !== undefined) {
120
- response.result = execution.result;
121
- }
122
- if (!execution.replayed && execution.result !== undefined) {
123
- if (staged !== undefined) {
124
- await cleanupDownloadedStagedUpload(staged);
125
- staged = undefined;
126
- }
127
- workspaceEvents.publish({
128
- event_type: "message.created",
129
- payload: { message: execution.result.message },
130
- invalidates,
131
- });
132
- workspaceEvents.publish({
133
- event_type: "query.invalidate",
134
- payload: {
135
- reason: "viewer_changed",
136
- message: "Reference files changed.",
137
- },
138
- invalidates,
139
- });
140
- options.controlPlane?.notifyScratchpadSourceChanged(execution.result.message);
141
- options.controlPlane?.startReferenceSummaryRefresh({
142
- paths: [execution.result.file.path],
143
- });
144
- }
145
- return response;
146
77
  }
147
78
  catch (error) {
148
79
  if (staged !== undefined) {
@@ -1,17 +1,7 @@
1
- import type { GetReferenceFilesRequest, GetReferenceFilesResponse, MessageCreatedEventPayload, QueryInvalidationHint, ReferenceFileProjection } from "@tutti/shared/schemas/api";
2
- import { readReferenceSummaryIndex, type ViewerTreeEntry } from "../../../../workspace-ops/index.js";
3
- export declare function toReferenceFileProjection(options: {
4
- workspaceRoot: string;
5
- entry: ViewerTreeEntry;
6
- summaryIndex?: ReturnType<typeof readReferenceSummaryIndex>;
7
- now?: () => Date;
8
- }): ReferenceFileProjection;
1
+ import type { GetReferenceFilesRequest, GetReferenceFilesResponse, QueryInvalidationHint } from "@tutti/shared/schemas/api";
9
2
  export declare function readReferenceFilesResponse(options: {
10
3
  workspaceRoot: string;
11
4
  now?: () => Date;
12
5
  }, query: GetReferenceFilesRequest): GetReferenceFilesResponse;
13
- export declare function referenceFilesChangedInvalidates(): QueryInvalidationHint[];
14
6
  export declare function referenceSummaryProcedureInvalidates(): QueryInvalidationHint[];
15
- export declare function referenceUploadMessageBody(file: ReferenceFileProjection): string;
16
- export declare function referenceFileMessageRef(file: ReferenceFileProjection): NonNullable<NonNullable<MessageCreatedEventPayload["message"]["refs"]>["reference_file"]>;
17
7
  //# sourceMappingURL=reference-files.d.ts.map
@@ -1,61 +1,6 @@
1
- import { REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, VIEWER_HARD_READ_LIMIT_BYTES, WorkspaceOpsError, readReferenceFileEntries, readReferenceSummaryIndex, readViewerFile, readViewerSnapshot, referenceFileCategoryForPath, referenceMediaTypeForPath, referenceSummaryProjectionForEntry, } from "../../../../workspace-ops/index.js";
1
+ import { toReferenceFileProjection } from "../../../../collaboration-ingestion/index.js";
2
+ import { REFERENCE_DIRECTORY_PATH, REFERENCE_FILES_DIRECTORY_PATH, REFERENCE_IMAGES_DIRECTORY_PATH, REFERENCE_INDEX_PATH, WorkspaceOpsError, readReferenceFileEntries, readReferenceSummaryIndex, readViewerFile, readViewerSnapshot, referenceFileCategoryForPath, } from "../../../../workspace-ops/index.js";
2
3
  import { paginateViewerItems, viewerPageLimit } from "./viewer-projections.js";
3
- function unreadableReasonForReferenceContent(content) {
4
- if (content.kind === "text") {
5
- return undefined;
6
- }
7
- return content.reason;
8
- }
9
- function referenceTextReadProjection(workspaceRoot, entry, now) {
10
- if (entry.size_bytes !== undefined && entry.size_bytes > VIEWER_HARD_READ_LIMIT_BYTES) {
11
- return { can_read: false, blocked_reason: "too_large" };
12
- }
13
- try {
14
- const file = readViewerFile({
15
- workspaceRoot,
16
- path: entry.path,
17
- maxBytes: 1,
18
- ...(now === undefined ? {} : { now }),
19
- });
20
- const reason = unreadableReasonForReferenceContent(file.content);
21
- if (reason !== undefined) {
22
- return { can_read: false, blocked_reason: reason };
23
- }
24
- return { can_read: true };
25
- }
26
- catch {
27
- return { can_read: false, blocked_reason: "unknown" };
28
- }
29
- }
30
- export function toReferenceFileProjection(options) {
31
- const category = referenceFileCategoryForPath(options.entry.path) ?? "file";
32
- const summaryIndex = options.summaryIndex ?? readReferenceSummaryIndex({ workspaceRoot: options.workspaceRoot });
33
- const projection = {
34
- path: options.entry.path,
35
- name: options.entry.name,
36
- category,
37
- size_bytes: options.entry.size_bytes ?? 0,
38
- text_read: referenceTextReadProjection(options.workspaceRoot, options.entry, options.now),
39
- summary: referenceSummaryProjectionForEntry({
40
- entry: options.entry,
41
- summaries: summaryIndex,
42
- }),
43
- };
44
- if (options.entry.blob_oid !== undefined) {
45
- projection.blob_oid = options.entry.blob_oid;
46
- }
47
- if (options.entry.extension !== undefined) {
48
- projection.extension = options.entry.extension;
49
- }
50
- if (options.entry.language_hint !== undefined) {
51
- projection.language_hint = options.entry.language_hint;
52
- }
53
- const mediaType = referenceMediaTypeForPath(options.entry.path);
54
- if (mediaType !== undefined) {
55
- projection.media_type = mediaType;
56
- }
57
- return projection;
58
- }
59
4
  function referenceDirectoryProjection(options) {
60
5
  if (options.directoryStatus === "missing") {
61
6
  return {
@@ -171,37 +116,7 @@ export function readReferenceFilesResponse(options, query) {
171
116
  page: page.page,
172
117
  };
173
118
  }
174
- export function referenceFilesChangedInvalidates() {
175
- return [
176
- { kind: "viewer", path: REFERENCE_DIRECTORY_PATH },
177
- { kind: "messages", scope: "main_chat" },
178
- { kind: "execution_status" },
179
- { kind: "workspace" },
180
- { kind: "read_state" },
181
- { kind: "bootstrap" },
182
- ];
183
- }
184
119
  export function referenceSummaryProcedureInvalidates() {
185
120
  return [{ kind: "execution_status" }, { kind: "bootstrap" }];
186
121
  }
187
- export function referenceUploadMessageBody(file) {
188
- return file.category === "image" ? `Uploaded image: ${file.name}` : `Uploaded file: ${file.name}`;
189
- }
190
- export function referenceFileMessageRef(file) {
191
- const ref = {
192
- path: file.path,
193
- name: file.name,
194
- source: "human_upload",
195
- category: file.category,
196
- size_bytes: file.size_bytes,
197
- text_read: file.text_read,
198
- };
199
- if (file.media_type !== undefined) {
200
- ref.media_type = file.media_type;
201
- }
202
- if (file.blob_oid !== undefined) {
203
- ref.blob_oid = file.blob_oid;
204
- }
205
- return ref;
206
- }
207
122
  //# sourceMappingURL=reference-files.js.map
@@ -415,10 +415,12 @@ export declare const SessionResponseSchema: {
415
415
  readonly required: readonly ["client_session_id", "transport", "connected_at"];
416
416
  readonly additionalProperties: false;
417
417
  readonly properties: {
418
- readonly client_session_id: import("@sinclair/typebox").TString;
418
+ readonly client_session_id: {
419
+ readonly oneOf: readonly [import("@sinclair/typebox").TString, import("@sinclair/typebox").TString];
420
+ };
419
421
  readonly transport: {
420
422
  readonly type: "string";
421
- readonly enum: readonly ["relay_tunnel"];
423
+ readonly enum: readonly ["relay_tunnel", "feishu_platform_session"];
422
424
  };
423
425
  readonly connected_at: import("@sinclair/typebox").TString;
424
426
  };
@@ -859,10 +861,12 @@ export declare const BootstrapResponseSchema: {
859
861
  readonly required: readonly ["client_session_id", "transport", "connected_at"];
860
862
  readonly additionalProperties: false;
861
863
  readonly properties: {
862
- readonly client_session_id: import("@sinclair/typebox").TString;
864
+ readonly client_session_id: {
865
+ readonly oneOf: readonly [import("@sinclair/typebox").TString, import("@sinclair/typebox").TString];
866
+ };
863
867
  readonly transport: {
864
868
  readonly type: "string";
865
- readonly enum: readonly ["relay_tunnel"];
869
+ readonly enum: readonly ["relay_tunnel", "feishu_platform_session"];
866
870
  };
867
871
  readonly connected_at: import("@sinclair/typebox").TString;
868
872
  };
@@ -1,4 +1,4 @@
1
- import { ApprovalRequestIdSchema, ClarificationRoundRefSchema, ClarificationThreadRefSchema, IdempotencyKeySchema, IsoDateTimeStringSchema, ProjectIdSchema, RelayAccountRefSchema, RelayClientSessionIdSchema, RelayProjectRefSchema, RecoverySummaryIdSchema, TaskIdSchema, } from "@tutti/shared/schemas/domain";
1
+ import { ApprovalRequestIdSchema, ClarificationRoundRefSchema, ClarificationThreadRefSchema, IdempotencyKeySchema, IsoDateTimeStringSchema, ProjectIdSchema, RelayAccountRefSchema, RelayClientSessionIdSchema, RelayPlatformSessionRefSchema, RelayProjectRefSchema, RecoverySummaryIdSchema, TaskIdSchema, } from "@tutti/shared/schemas/domain";
2
2
  import { ActiveClarificationProjectionSchema, ActiveApprovalProjectionSchema, ExecutionStatusProjectionSchema, MessageWindowSchema, ProjectTimelineResponseSchema, ReadStateSummaryProjectionSchema, ScratchpadProjectionSchema, WorklistProjectionSchema, } from "@tutti/shared/schemas/api";
3
3
  import { ACCOUNT_AVATAR_COLORS } from "@tutti/shared/domain";
4
4
  import { HOST_MAINLINE_BRANCH } from "./constants.js";
@@ -212,8 +212,13 @@ export const SessionResponseSchema = {
212
212
  required: ["client_session_id", "transport", "connected_at"],
213
213
  additionalProperties: false,
214
214
  properties: {
215
- client_session_id: RelayClientSessionIdSchema,
216
- transport: { type: "string", enum: ["relay_tunnel"] },
215
+ client_session_id: {
216
+ oneOf: [RelayClientSessionIdSchema, RelayPlatformSessionRefSchema],
217
+ },
218
+ transport: {
219
+ type: "string",
220
+ enum: ["relay_tunnel", "feishu_platform_session"],
221
+ },
217
222
  connected_at: IsoDateTimeStringSchema,
218
223
  },
219
224
  },
@@ -1,4 +1,4 @@
1
- import { requireRelaySessionContext, TRUSTED_RELAY_METADATA_REQUEST_HEADER, } from "../../../session/relay-session-context.js";
1
+ import { readFeishuPlatformSessionContext, readRelaySessionContext, requireFeishuPlatformSessionContext, requireRelaySessionContext, TRUSTED_RELAY_METADATA_REQUEST_HEADER, } from "../../../session/relay-session-context.js";
2
2
  import { HostProjectForbiddenError, HostProjectStoreUnavailableError } from "./errors.js";
3
3
  import { headerToString } from "./headers.js";
4
4
  function readTrustedMetadata(request, store) {
@@ -9,8 +9,40 @@ function readTrustedMetadata(request, store) {
9
9
  return store.consume(requestId);
10
10
  }
11
11
  export function requireHostProjectSession(request, options) {
12
- const context = requireRelaySessionContext(readTrustedMetadata(request, options.trustedMetadataStore));
12
+ const surface = options.surfaceProfile?.kind ?? "standalone";
13
+ const metadata = readTrustedMetadata(request, options.trustedMetadataStore);
13
14
  const relayState = options.relay.getState();
15
+ if (surface === "feishu") {
16
+ if (readRelaySessionContext(metadata).kind === "present") {
17
+ throw new HostProjectForbiddenError();
18
+ }
19
+ const context = requireFeishuPlatformSessionContext(metadata);
20
+ if (relayState.relay_project_ref !== undefined &&
21
+ context.membership.relay_project_ref !== relayState.relay_project_ref) {
22
+ throw new HostProjectForbiddenError();
23
+ }
24
+ return {
25
+ account: {
26
+ account_ref: context.account_ref,
27
+ display_name: context.display_name,
28
+ avatar: context.avatar,
29
+ },
30
+ membership: {
31
+ label: context.membership.label,
32
+ relay_project_ref: context.membership.relay_project_ref,
33
+ joined_at: context.membership.joined_at,
34
+ },
35
+ client_session: {
36
+ client_session_id: context.platform_session_ref,
37
+ transport: "feishu_platform_session",
38
+ connected_at: context.platform_session_created_at,
39
+ },
40
+ };
41
+ }
42
+ if (readFeishuPlatformSessionContext(metadata).kind === "present") {
43
+ throw new HostProjectForbiddenError();
44
+ }
45
+ const context = requireRelaySessionContext(metadata);
14
46
  if (relayState.relay_project_ref !== undefined &&
15
47
  context.membership.relay_project_ref !== relayState.relay_project_ref) {
16
48
  throw new HostProjectForbiddenError();
@@ -9,6 +9,7 @@ import type { TrustedRelaySessionMetadataStore } from "../../../session/relay-se
9
9
  import type { WorkspaceEventBus } from "../../workspace-events.js";
10
10
  import type { HOST_MAINLINE_BRANCH } from "./constants.js";
11
11
  import type { GetProviderModelsResponse, ProviderUsageProjection, RelayUploadProjection, UpdateProviderDefaultModelDisposition, UpdateProviderDefaultModelPayload } from "@tutti/shared/schemas/api";
12
+ import type { ProjectSurfaceProfile } from "@tutti/shared/domain";
12
13
  export type ResolvedStagedUpload = {
13
14
  upload: RelayUploadProjection;
14
15
  get: {
@@ -82,6 +83,7 @@ export type HostProviderModelService = {
82
83
  }>;
83
84
  };
84
85
  export type HostProjectApiRouteOptions = {
86
+ surfaceProfile?: ProjectSurfaceProfile;
85
87
  artifactPreview?: Omit<ArtifactPreviewManagerOptions, "onStatusChange">;
86
88
  artifactPreviewManager?: ArtifactPreviewManager;
87
89
  project: {
@@ -134,7 +136,7 @@ export type HostSessionProjection = {
134
136
  };
135
137
  client_session: {
136
138
  client_session_id: string;
137
- transport: "relay_tunnel";
139
+ transport: "relay_tunnel" | "feishu_platform_session";
138
140
  connected_at: string;
139
141
  };
140
142
  };
@@ -76,13 +76,19 @@ export function buildBootstrapSnapshots(options, eventsNow, session) {
76
76
  if (options.store === undefined) {
77
77
  return {};
78
78
  }
79
- return {
80
- main_chat: readMainChatMessageWindow(options.store.db),
79
+ const sharedSnapshots = {
81
80
  scratchpad: readScratchpadProjection(options.store.db),
82
81
  worklist: readWorklistProjection(options.store.db),
83
82
  active_clarification: readActiveClarificationProjection(options.store.db),
84
83
  active_approval: options.approvalManager?.getActiveApproval() ?? { state: "none" },
85
84
  execution_status: options.controlPlane?.getExecutionStatus() ?? createIdleExecutionStatusProjection(eventsNow),
85
+ };
86
+ if ((options.surfaceProfile?.kind ?? "standalone") === "feishu") {
87
+ return sharedSnapshots;
88
+ }
89
+ return {
90
+ main_chat: readMainChatMessageWindow(options.store.db),
91
+ ...sharedSnapshots,
86
92
  read_state: readReadStateSummary(options.store.db, {
87
93
  account_ref: session.account.account_ref,
88
94
  session_created_at: session.client_session.connected_at,
@@ -69,7 +69,9 @@ export async function restorePackageUpdateHosts(options) {
69
69
  failed.push(projectId);
70
70
  continue;
71
71
  }
72
- spawnDetachedHost({
72
+ const startup = spawnDetachedHost({
73
+ tuttiHome: options.tuttiHome,
74
+ projectId,
73
75
  workspaceRoot: project.workspace_root,
74
76
  env: { ...options.env, TUTTI_HOME: options.tuttiHome },
75
77
  inheritProcessEnv: false,
@@ -79,6 +81,7 @@ export async function restorePackageUpdateHosts(options) {
79
81
  projectId,
80
82
  workspaceRoot: project.workspace_root,
81
83
  inviteMode: "preserve",
84
+ startupAttemptRef: startup.startup_attempt_ref,
82
85
  ...(options.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
83
86
  });
84
87
  restored.push(projectId);