@xfey/tutti 0.1.111 → 0.1.112
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/approvals/decision-command.d.ts +2 -2
- package/dist/chat-assistant/index.d.ts +1 -1
- package/dist/chat-assistant/index.js +19 -6
- package/dist/collaboration-ingestion/clarification.d.ts +25 -0
- package/dist/collaboration-ingestion/clarification.js +63 -0
- package/dist/collaboration-ingestion/external-source.d.ts +2 -0
- package/dist/collaboration-ingestion/external-source.js +33 -0
- package/dist/collaboration-ingestion/index.d.ts +2 -0
- package/dist/collaboration-ingestion/index.js +2 -0
- package/dist/collaboration-ingestion/platform-clarification.d.ts +10 -0
- package/dist/collaboration-ingestion/platform-clarification.js +42 -0
- package/dist/collaboration-state/clarification-messages.js +3 -2
- package/dist/collaboration-state/clarification-records.d.ts +1 -1
- package/dist/collaboration-state/clarification-records.js +7 -5
- package/dist/collaboration-state/clarification-rounds.js +5 -5
- package/dist/collaboration-state/clarification-snapshot.d.ts +228 -0
- package/dist/collaboration-state/clarification-snapshot.js +29 -0
- package/dist/collaboration-state/clarification-successors.js +6 -6
- package/dist/collaboration-state/index.d.ts +1 -0
- package/dist/collaboration-state/index.js +1 -0
- package/dist/collaboration-state/run-recording.js +1 -1
- package/dist/collaboration-state/task-compile-context.d.ts +1 -1
- package/dist/collaboration-state/types.d.ts +1 -0
- package/dist/control-plane/clarification-commands.d.ts +3 -2
- package/dist/control-plane/clarification-commands.js +13 -4
- package/dist/control-plane/follow-up-context.js +8 -14
- package/dist/control-plane/index.d.ts +3 -2
- package/dist/control-plane/task-compile-clarification-context.js +7 -2
- package/dist/control-plane/types.d.ts +1 -0
- package/dist/control-plane/workflows/openai.js +5 -4
- package/dist/platform-outbound/repository.js +83 -4
- package/dist/server-shell/cli/host-lifecycle.js +1 -0
- package/dist/server-shell/cli/host-server-runtime.d.ts +2 -1
- package/dist/server-shell/cli/host-server-runtime.js +4 -2
- package/dist/server-shell/cli/relay-registration.js +1 -0
- package/dist/server-shell/http/host-tunnel.d.ts +2 -1
- package/dist/server-shell/http/host-tunnel.js +4 -1
- package/dist/server-shell/http/integration-clarification-dispatch.d.ts +6 -0
- package/dist/server-shell/http/integration-clarification-dispatch.js +43 -0
- package/dist/server-shell/http/routes/project-api/clarification-routes.js +16 -42
- package/dist/server-shell/http/routes/project-api/openapi-collaboration-routes.d.ts +2 -0
- package/dist/server-shell/http/routes/project-api/openapi-workspace-routes.d.ts +1 -0
- package/dist/server-shell/http/routes/project-api/openapi.d.ts +3 -0
- package/dist/server-shell/http/routes/project-api/schemas.d.ts +1 -0
- package/migrations/0022_clarification_projection_cursors.sql +7 -0
- package/node_modules/@tutti/relay-client/dist/host-control.d.ts +1 -0
- package/node_modules/@tutti/relay-client/dist/host-control.js +3 -0
- package/node_modules/@tutti/shared/dist/schemas/api/clarifications.d.ts +13 -1
- package/node_modules/@tutti/shared/dist/schemas/api/clarifications.js +9 -2
- package/node_modules/@tutti/shared/dist/schemas/internal/index.d.ts +2 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/index.js +2 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/platform-clarification-content.d.ts +15 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/platform-clarification-content.js +24 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/platform-clarification.d.ts +354 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/platform-clarification.js +94 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/platform-integration.d.ts +89 -3
- package/node_modules/@tutti/shared/dist/schemas/internal/platform-integration.js +15 -3
- package/node_modules/@tutti/shared/dist/utils/clarification-participants.d.ts +4 -0
- package/node_modules/@tutti/shared/dist/utils/clarification-participants.js +11 -0
- package/node_modules/@tutti/shared/dist/utils/index.d.ts +1 -0
- package/node_modules/@tutti/shared/dist/utils/index.js +1 -0
- package/package.json +1 -1
- package/prompts/procedures/follow-up-check.md +4 -4
- package/web/assets/{homepage-motion-scene-TmkKf8wQ.js → homepage-motion-scene-BwdH6Iss.js} +1 -1
- package/web/assets/index-B29RuNsd.js +69 -0
- package/web/index.html +1 -1
- package/web/assets/index-B1oL1p9_.js +0 -69
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { mapMessageRow } from "./messages.js";
|
|
2
|
+
/** Round messages are append-only; the eligible answer count is a stable CAS revision. */
|
|
3
|
+
export function readClarificationAnswerRevision(db, roundId) {
|
|
4
|
+
return db
|
|
5
|
+
.prepare(`
|
|
6
|
+
SELECT COUNT(*) AS count FROM messages
|
|
7
|
+
WHERE scope_kind = 'clarification_round' AND clarification_round_id = ?
|
|
8
|
+
AND message_kind = 'user_text'
|
|
9
|
+
AND (author_kind = 'human' OR
|
|
10
|
+
(author_kind = 'system' AND json_extract(refs_json, '$.development_operator') = 1))
|
|
11
|
+
`)
|
|
12
|
+
.get(roundId).count;
|
|
13
|
+
}
|
|
14
|
+
/** Read the immutable submitted refs, not a first page or messages appended after sealing. */
|
|
15
|
+
export function readSubmittedClarificationMessages(db, roundId) {
|
|
16
|
+
return db
|
|
17
|
+
.prepare(`
|
|
18
|
+
SELECT messages.* FROM clarification_rounds AS rounds
|
|
19
|
+
JOIN json_each(rounds.submitted_message_refs_json) AS submitted
|
|
20
|
+
JOIN messages ON messages.id = submitted.value
|
|
21
|
+
WHERE rounds.id = ? AND rounds.status = 'sealed'
|
|
22
|
+
AND messages.scope_kind = 'clarification_round'
|
|
23
|
+
AND messages.clarification_round_id = rounds.id
|
|
24
|
+
ORDER BY CAST(submitted.key AS INTEGER)
|
|
25
|
+
`)
|
|
26
|
+
.all(roundId)
|
|
27
|
+
.map((row) => mapMessageRow(row));
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=clarification-snapshot.js.map
|
|
@@ -15,7 +15,7 @@ export function submitClarificationRound(db, input) {
|
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
if (row.status === "sealed") {
|
|
18
|
-
round = mapClarificationRoundRow(row);
|
|
18
|
+
round = mapClarificationRoundRow(row, db);
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
const messageRows = db
|
|
@@ -64,7 +64,7 @@ export function materializeClarificationSuccessor(db, input) {
|
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
if (row.successor_status === "materialized") {
|
|
67
|
-
round = mapClarificationRoundRow(row);
|
|
67
|
+
round = mapClarificationRoundRow(row, db);
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
db.prepare(`
|
|
@@ -120,7 +120,7 @@ export function materializeTaskBoundRunSuccessor(db, input) {
|
|
|
120
120
|
result = {
|
|
121
121
|
kind: "already_materialized",
|
|
122
122
|
thread: mapClarificationThreadRow(thread),
|
|
123
|
-
round: mapClarificationRoundRow(row),
|
|
123
|
+
round: mapClarificationRoundRow(row, db),
|
|
124
124
|
task: readTaskProjection(db, thread.owner_task_id),
|
|
125
125
|
};
|
|
126
126
|
return;
|
|
@@ -182,7 +182,7 @@ export function materializeTaskBoundRunSuccessor(db, input) {
|
|
|
182
182
|
result = {
|
|
183
183
|
kind: "materialized",
|
|
184
184
|
thread: mapClarificationThreadRow(updatedThread),
|
|
185
|
-
round: mapClarificationRoundRow(updatedRound),
|
|
185
|
+
round: mapClarificationRoundRow(updatedRound, db),
|
|
186
186
|
task: updatedTask,
|
|
187
187
|
};
|
|
188
188
|
})();
|
|
@@ -210,7 +210,7 @@ export function closeTaskBoundClarificationAsRetaskingRequired(db, input) {
|
|
|
210
210
|
return {
|
|
211
211
|
kind: "already_materialized",
|
|
212
212
|
thread: mapClarificationThreadRow(thread),
|
|
213
|
-
round: mapClarificationRoundRow(row),
|
|
213
|
+
round: mapClarificationRoundRow(row, db),
|
|
214
214
|
task: readTaskProjection(tx, taskId),
|
|
215
215
|
task_detail: readTaskDetailProjection(tx, taskId),
|
|
216
216
|
};
|
|
@@ -308,7 +308,7 @@ export function closeTaskBoundClarificationAsRetaskingRequired(db, input) {
|
|
|
308
308
|
return {
|
|
309
309
|
kind: "closed",
|
|
310
310
|
thread: mapClarificationThreadRow(updatedThread),
|
|
311
|
-
round: mapClarificationRoundRow(updatedRound),
|
|
311
|
+
round: mapClarificationRoundRow(updatedRound, db),
|
|
312
312
|
task,
|
|
313
313
|
task_detail: taskDetail,
|
|
314
314
|
message,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { CollaborationStateCursorError } from "./serialization.js";
|
|
2
|
+
export { readClarificationAnswerRevision, readSubmittedClarificationMessages, } from "./clarification-snapshot.js";
|
|
2
3
|
export { createIdleExecutionStatusProjection } from "./execution-status.js";
|
|
3
4
|
export { createMainChatAgentMessage, createMainChatMessage, createMainChatSystemMessage, ensureInitialProjectWelcomeMessage, hasMainChatArtifactPreviewMessage, hasMainChatTaskResultMessage, hasMainChatWorklistCompileMessage, hasMainChatWorklistFeedbackMessage, readCompletedWorklistFeedbackTaskIds, readMessageProjectionById, readMainChatMessageWindow, readMainChatMessagesBeforeCursor, readMainChatMessagesForScratchpadCycle, } from "./messages.js";
|
|
4
5
|
export { createEmptyScratchpadProjection, readScratchpadReceiptSourceRefs, readScratchpadProjection, upsertScratchpadProjection, } from "./scratchpad.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { CollaborationStateCursorError } from "./serialization.js";
|
|
2
|
+
export { readClarificationAnswerRevision, readSubmittedClarificationMessages, } from "./clarification-snapshot.js";
|
|
2
3
|
export { createIdleExecutionStatusProjection } from "./execution-status.js";
|
|
3
4
|
export { createMainChatAgentMessage, createMainChatMessage, createMainChatSystemMessage, ensureInitialProjectWelcomeMessage, hasMainChatArtifactPreviewMessage, hasMainChatTaskResultMessage, hasMainChatWorklistCompileMessage, hasMainChatWorklistFeedbackMessage, readCompletedWorklistFeedbackTaskIds, readMessageProjectionById, readMainChatMessageWindow, readMainChatMessagesBeforeCursor, readMainChatMessagesForScratchpadCycle, } from "./messages.js";
|
|
4
5
|
export { createEmptyScratchpadProjection, readScratchpadReceiptSourceRefs, readScratchpadProjection, upsertScratchpadProjection, } from "./scratchpad.js";
|
|
@@ -227,7 +227,7 @@ export function recordRunNeedsHumanResult(db, input) {
|
|
|
227
227
|
task: updatedTask,
|
|
228
228
|
task_detail: taskDetail,
|
|
229
229
|
thread: mapClarificationThreadRow(thread),
|
|
230
|
-
round: mapClarificationRoundRow(round),
|
|
230
|
+
round: mapClarificationRoundRow(round, db),
|
|
231
231
|
card_message: mapMessageRow(cardMessage, tx),
|
|
232
232
|
opening_message: mapMessageRow(openingMessage),
|
|
233
233
|
};
|
|
@@ -15,8 +15,8 @@ export declare function completeTaskCompileContext(db: SqliteDatabase, input: Co
|
|
|
15
15
|
updated_by_activity_ref?: string;
|
|
16
16
|
submitted_by_activity_ref?: string;
|
|
17
17
|
submitted_by_workflow_ref?: string;
|
|
18
|
-
id: "current";
|
|
19
18
|
state: "ready" | "empty" | "submitted";
|
|
19
|
+
id: "current";
|
|
20
20
|
topic: string;
|
|
21
21
|
background_summary: string;
|
|
22
22
|
current_consensus: string[];
|
|
@@ -279,6 +279,7 @@ export type CreateClarificationRoundMessageInput = {
|
|
|
279
279
|
round_id: ClarificationRoundRef;
|
|
280
280
|
body: string;
|
|
281
281
|
author: MessageAuthorInput;
|
|
282
|
+
refs?: MessageProjection["refs"];
|
|
282
283
|
now?: () => Date;
|
|
283
284
|
};
|
|
284
285
|
export type CreateClarificationRoundAgentMessageInput = {
|
|
@@ -2,13 +2,14 @@ import type { ClarificationRoundRef, IdempotencyKey } from "@tutti/shared/ids";
|
|
|
2
2
|
import type { SendClarificationRoundMessageDisposition, SendClarificationRoundMessagePayload, SendClarificationRoundMessageResult, SubmitClarificationRoundDisposition, SubmitClarificationRoundPayload, SubmitClarificationRoundResult } from "@tutti/shared/schemas/api";
|
|
3
3
|
import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js";
|
|
4
4
|
import { type HostProjectStore } from "../store/index.js";
|
|
5
|
-
import type { ControlPlaneCommandResult
|
|
5
|
+
import type { ControlPlaneCommandResult } from "./types.js";
|
|
6
|
+
import type { TrustedCollaborationAuthor } from "../collaboration-ingestion/types.js";
|
|
6
7
|
export declare function runSendClarificationRoundMessage(options: {
|
|
7
8
|
store: HostProjectStore;
|
|
8
9
|
events: WorkspaceEventBus;
|
|
9
10
|
roundId: ClarificationRoundRef;
|
|
10
11
|
payload: SendClarificationRoundMessagePayload;
|
|
11
|
-
author:
|
|
12
|
+
author: TrustedCollaborationAuthor;
|
|
12
13
|
now: () => Date;
|
|
13
14
|
}): ControlPlaneCommandResult<SendClarificationRoundMessageDisposition, SendClarificationRoundMessageResult>;
|
|
14
15
|
export declare function runSubmitClarificationRound(options: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createClarificationRoundMessage, readActiveClarificationProjection, submitClarificationRound, } from "../collaboration-state/index.js";
|
|
1
|
+
import { createClarificationRoundMessage, readActiveClarificationProjection, readClarificationAnswerRevision, submitClarificationRound, } from "../collaboration-state/index.js";
|
|
2
2
|
import { withHostStoreTransaction } from "../store/index.js";
|
|
3
3
|
import { enqueueControlPlaneIntent } from "./dispatch-intents.js";
|
|
4
4
|
import { clarificationInvalidates } from "./invalidations.js";
|
|
@@ -19,11 +19,14 @@ export function runSendClarificationRoundMessage(options) {
|
|
|
19
19
|
round_id: options.roundId,
|
|
20
20
|
body: options.payload.body,
|
|
21
21
|
author: {
|
|
22
|
-
kind: "human",
|
|
22
|
+
kind: options.author.kind === "development_operator" ? "system" : "human",
|
|
23
23
|
ref: options.author.account_ref,
|
|
24
24
|
display_name: options.author.display_name,
|
|
25
25
|
...(options.author.avatar === undefined ? {} : { avatar: options.author.avatar }),
|
|
26
26
|
},
|
|
27
|
+
...(options.author.kind === "development_operator"
|
|
28
|
+
? { refs: { development_operator: true } }
|
|
29
|
+
: {}),
|
|
27
30
|
now: options.now,
|
|
28
31
|
});
|
|
29
32
|
options.events.publish({
|
|
@@ -41,12 +44,11 @@ export function runSendClarificationRoundMessage(options) {
|
|
|
41
44
|
invalidates: clarificationInvalidates(options.roundId, active.thread.id),
|
|
42
45
|
});
|
|
43
46
|
return {
|
|
44
|
-
disposition: { kind: "created" },
|
|
47
|
+
disposition: { kind: "created", message_id: message.id },
|
|
45
48
|
result: { message },
|
|
46
49
|
};
|
|
47
50
|
}
|
|
48
51
|
export function runSubmitClarificationRound(options) {
|
|
49
|
-
void options.payload;
|
|
50
52
|
const active = readActiveClarificationProjection(options.store.db);
|
|
51
53
|
if (active.state === "none") {
|
|
52
54
|
return { disposition: { kind: "chain_closed" } };
|
|
@@ -71,6 +73,13 @@ export function runSubmitClarificationRound(options) {
|
|
|
71
73
|
result: { round: active.round },
|
|
72
74
|
};
|
|
73
75
|
}
|
|
76
|
+
const answerRevision = readClarificationAnswerRevision(options.store.db, options.roundId);
|
|
77
|
+
if (options.payload.expected_answer_revision !== undefined &&
|
|
78
|
+
options.payload.expected_answer_revision !== answerRevision) {
|
|
79
|
+
return { disposition: { kind: "answers_changed", current_answer_revision: answerRevision } };
|
|
80
|
+
}
|
|
81
|
+
if (answerRevision === 0)
|
|
82
|
+
return { disposition: { kind: "no_answers" } };
|
|
74
83
|
const sealed = withHostStoreTransaction(options.store.db, (tx) => {
|
|
75
84
|
const submitted = submitClarificationRound(tx, {
|
|
76
85
|
round_id: options.roundId,
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { clarificationAnswerRole } from "@tutti/shared/utils";
|
|
2
|
+
import { readSubmittedClarificationMessages, readClarificationRoundProjection, readClarificationRoundsForThread, readTaskDetailProjection, readTaskRunResultsPage, } from "../collaboration-state/index.js";
|
|
2
3
|
import { toDomainRunLineage } from "./run-lineage.js";
|
|
3
4
|
const CONTINUATION_CLARIFICATION_MESSAGE_LIMIT = 200;
|
|
4
5
|
export function readFollowUpCheckInputFromStore(input) {
|
|
5
6
|
const taskDetail = readTaskDetailProjection(input.db, input.task_id);
|
|
6
7
|
const round = readClarificationRoundProjection(input.db, input.round_id);
|
|
7
|
-
const messagesWindow = readClarificationRoundMessagesWindow(input.db, input.round_id, {
|
|
8
|
-
anchor: "start",
|
|
9
|
-
});
|
|
10
8
|
const latestRunResult = readTaskRunResultsPage(input.db, input.task_id, {
|
|
11
9
|
limit: 1,
|
|
12
10
|
})?.items[0];
|
|
@@ -29,7 +27,7 @@ export function readFollowUpCheckInputFromStore(input) {
|
|
|
29
27
|
clarification_round_ref: input.round_id,
|
|
30
28
|
clarification_request_payload: round.request_payload,
|
|
31
29
|
submitted_message_refs: (round.submitted_message_refs ?? []),
|
|
32
|
-
round_messages:
|
|
30
|
+
round_messages: readSubmittedClarificationMessages(input.db, input.round_id),
|
|
33
31
|
latest_run_result: latestRunResult,
|
|
34
32
|
run_lineage: toDomainRunLineage(latestRunResult.run_lineage),
|
|
35
33
|
},
|
|
@@ -51,18 +49,14 @@ export function readRunContinuationContextFromStore(input) {
|
|
|
51
49
|
}
|
|
52
50
|
let messagesTruncated = false;
|
|
53
51
|
const resolvedRounds = rounds.map((round) => {
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
56
|
-
messagesWindow.window.has_older ||
|
|
57
|
-
messagesWindow.window.has_newer) {
|
|
52
|
+
const submitted = readSubmittedClarificationMessages(input.db, round.id).filter((message) => clarificationAnswerRole(message) !== null);
|
|
53
|
+
if (submitted.length > CONTINUATION_CLARIFICATION_MESSAGE_LIMIT)
|
|
58
54
|
messagesTruncated = true;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const answerMessages = (messagesWindow?.items ?? [])
|
|
62
|
-
.filter((message) => message.author.kind === "human")
|
|
63
|
-
.filter((message) => submittedMessageRefs.size === 0 || submittedMessageRefs.has(message.id))
|
|
55
|
+
const answerMessages = submitted
|
|
56
|
+
.slice(0, CONTINUATION_CLARIFICATION_MESSAGE_LIMIT)
|
|
64
57
|
.map((message) => ({
|
|
65
58
|
author: message.author,
|
|
59
|
+
role: clarificationAnswerRole(message),
|
|
66
60
|
text: message.body,
|
|
67
61
|
created_at: message.created_at,
|
|
68
62
|
}));
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type ActivityRef, type ClarificationRoundRef, type IdempotencyKey, type TaskId, type WorkflowInvocationRef } from "@tutti/shared/ids";
|
|
2
|
+
import type { TrustedCollaborationAuthor } from "../collaboration-ingestion/types.js";
|
|
2
3
|
import type { ExecutionStatusProjection, MessageProjection, ContextSyncDisposition, ContextSyncResult, RefreshScratchpadDisposition, RefreshScratchpadResult, RunSchedulerNowDisposition, RunSchedulerNowPayload, RetryTaskDisposition, RetryTaskPayload, RefreshReferenceSummariesDisposition, RefreshReferenceSummariesPayload, RefreshReferenceSummariesResult, SendClarificationRoundMessageDisposition, SendClarificationRoundMessagePayload, SendClarificationRoundMessageResult, SubmitClarificationRoundDisposition, SubmitClarificationRoundPayload, SubmitClarificationRoundResult, SubmitWorklistDisposition, SubmitWorklistPayload, SubmitWorklistResult, ProcedureLane } from "@tutti/shared/schemas/api";
|
|
3
4
|
import type { ContextSyncWorkflowTrigger } from "./workflows/index.js";
|
|
4
|
-
import type { ControlPlaneCommandResult, Phase5ControlPlaneOptions, StartupRecoveryOptions, StartupRecoveryResult
|
|
5
|
+
import type { ControlPlaneCommandResult, Phase5ControlPlaneOptions, StartupRecoveryOptions, StartupRecoveryResult } from "./types.js";
|
|
5
6
|
import { type ProjectBriefRefreshReason, type RefreshProjectBriefProjectionResult } from "./project-brief-refresh.js";
|
|
6
7
|
export type { ControlPlaneCommandResult, ControlPlaneLogger, Phase5ControlPlaneOptions, RunCorrectionContext, RunContinuationContext, RunDispatchContext, RunDispatchTrigger, RunPipelineInput, RunPipelineResolution, RunPipelineResolver, RunPipelineResult, RunPipelineRunner, StartupRecoveryOptions, StartupRecoveryResult, TrustedHumanAuthor, } from "./types.js";
|
|
7
8
|
export declare class Phase5ControlPlane {
|
|
@@ -50,7 +51,7 @@ export declare class Phase5ControlPlane {
|
|
|
50
51
|
startContextSyncAnalysis(trigger?: ContextSyncWorkflowTrigger): ControlPlaneCommandResult<ContextSyncDisposition, ContextSyncResult>;
|
|
51
52
|
startReferenceSummaryRefresh(payload?: RefreshReferenceSummariesPayload): ControlPlaneCommandResult<RefreshReferenceSummariesDisposition, RefreshReferenceSummariesResult>;
|
|
52
53
|
private tryStartPendingReferenceSummaryRefresh;
|
|
53
|
-
sendClarificationRoundMessage(roundId: ClarificationRoundRef, payload: SendClarificationRoundMessagePayload, author:
|
|
54
|
+
sendClarificationRoundMessage(roundId: ClarificationRoundRef, payload: SendClarificationRoundMessagePayload, author: TrustedCollaborationAuthor): ControlPlaneCommandResult<SendClarificationRoundMessageDisposition, SendClarificationRoundMessageResult>;
|
|
54
55
|
submitClarificationRound(roundId: ClarificationRoundRef, payload: SubmitClarificationRoundPayload, commandId: IdempotencyKey): ControlPlaneCommandResult<SubmitClarificationRoundDisposition, SubmitClarificationRoundResult>;
|
|
55
56
|
waitForIdle(): Promise<void>;
|
|
56
57
|
private trackAsyncOwner;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clarificationAnswerRole } from "@tutti/shared/utils";
|
|
1
2
|
import { readActiveClarificationProjection, readClarificationRoundProjection, readClarificationRoundsForThread, readMessageProjectionById, readTaskCompileClarificationRoundsByWorkflowRef, } from "../collaboration-state/index.js";
|
|
2
3
|
function answerMessage(store, messageId, roundId) {
|
|
3
4
|
const message = readMessageProjectionById(store.db, messageId);
|
|
@@ -7,11 +8,15 @@ function answerMessage(store, messageId, roundId) {
|
|
|
7
8
|
(message.message_kind !== "user_text" && message.message_kind !== "agent_text")) {
|
|
8
9
|
return null;
|
|
9
10
|
}
|
|
10
|
-
const role = message.author.kind === "agent"
|
|
11
|
+
const role = message.author.kind === "agent" && message.message_kind === "agent_text"
|
|
12
|
+
? "tutti"
|
|
13
|
+
: clarificationAnswerRole(message);
|
|
14
|
+
if (role === null)
|
|
15
|
+
return null;
|
|
11
16
|
const displayName = message.author.display_name?.trim();
|
|
12
17
|
return {
|
|
13
18
|
role,
|
|
14
|
-
...(role
|
|
19
|
+
...(role !== "tutti" && displayName !== undefined && displayName !== ""
|
|
15
20
|
? { author_display_name: displayName }
|
|
16
21
|
: {}),
|
|
17
22
|
text: message.body,
|
|
@@ -2,6 +2,7 @@ import { copyFileSync, mkdtempSync } from "node:fs";
|
|
|
2
2
|
import { rm } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
|
+
import { clarificationAnswerRole } from "@tutti/shared/utils";
|
|
5
6
|
import { renderPromptTemplate } from "../../prompt-templates/index.js";
|
|
6
7
|
import { createOpenAiStructuredOutputClient, runOpenAiStructuredOutput, } from "../../providers/openai/sdk-procedure-runner.js";
|
|
7
8
|
import { readOpenAiApiKeyCredential } from "../../providers/openai/credential-store.js";
|
|
@@ -162,10 +163,10 @@ function referenceFileSummaryWorkflowOutput(output, originalPath) {
|
|
|
162
163
|
function followUpCheckPromptInput(input) {
|
|
163
164
|
const submittedMessageRefs = new Set(input.submitted_message_refs);
|
|
164
165
|
const answers = input.round_messages
|
|
165
|
-
.filter((message) => message
|
|
166
|
-
.filter((message) => submittedMessageRefs.
|
|
167
|
-
.
|
|
168
|
-
.
|
|
166
|
+
.filter((message) => clarificationAnswerRole(message) !== null)
|
|
167
|
+
.filter((message) => submittedMessageRefs.has(message.id))
|
|
168
|
+
.filter((message) => message.body.trim().length > 0)
|
|
169
|
+
.map((message) => ({ role: clarificationAnswerRole(message), text: message.body.trim() }));
|
|
169
170
|
return {
|
|
170
171
|
title: input.task_detail.task.title,
|
|
171
172
|
goal: input.task_detail.detail.contract.goal,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { createIdempotencyKey } from "@tutti/shared/ids";
|
|
1
|
+
import { createIdempotencyKey, } from "@tutti/shared/ids";
|
|
2
2
|
import { Value } from "@sinclair/typebox/value";
|
|
3
3
|
import { PlatformOutboundContentSchema, } from "@tutti/shared/schemas/internal";
|
|
4
|
+
import { readClarificationRoundProjection, readClarificationThreadResponse, } from "../collaboration-state/index.js";
|
|
4
5
|
const MAX_PLATFORM_OUTBOUND_TEXT_LENGTH = 4_000;
|
|
5
6
|
function boundedPlatformOutboundText(value) {
|
|
6
7
|
const characters = [...value.trim()];
|
|
@@ -134,10 +135,13 @@ export function materializePlatformStatusOutboundIntents(db, now = () => new Dat
|
|
|
134
135
|
subjectRef: roundRef,
|
|
135
136
|
targetKind: "project",
|
|
136
137
|
canonicalMessageId: candidate.id,
|
|
138
|
+
content: clarificationContent(db, roundRef),
|
|
137
139
|
now,
|
|
138
140
|
});
|
|
139
141
|
if (result?.created === true) {
|
|
140
142
|
created.push(result.intent);
|
|
143
|
+
const content = clarificationContent(db, roundRef);
|
|
144
|
+
db.prepare("INSERT OR IGNORE INTO platform_clarification_projection_cursors (round_id, binding_ref, version) VALUES (?, ?, ?)").run(roundRef, result.intent.binding_ref, content.version);
|
|
141
145
|
}
|
|
142
146
|
continue;
|
|
143
147
|
}
|
|
@@ -162,9 +166,61 @@ export function materializePlatformStatusOutboundIntents(db, now = () => new Dat
|
|
|
162
166
|
created.push(result.intent);
|
|
163
167
|
}
|
|
164
168
|
}
|
|
169
|
+
for (const cursor of db
|
|
170
|
+
.prepare("SELECT round_id, binding_ref, version FROM platform_clarification_projection_cursors WHERE closed = 0")
|
|
171
|
+
.all()) {
|
|
172
|
+
const content = clarificationContent(db, cursor.round_id);
|
|
173
|
+
if (content === null || readLatestFeishuDestination(db)?.binding_ref !== cursor.binding_ref) {
|
|
174
|
+
db.prepare("UPDATE platform_clarification_projection_cursors SET closed = 1 WHERE round_id = ?").run(cursor.round_id);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (content.version <= cursor.version)
|
|
178
|
+
continue;
|
|
179
|
+
const result = createPlatformStatusOutboundIntent(db, {
|
|
180
|
+
eventKind: "needs_input",
|
|
181
|
+
eventKey: `clarification_round:${cursor.round_id}:state:${content.version}`,
|
|
182
|
+
subjectKind: "clarification_round",
|
|
183
|
+
subjectRef: cursor.round_id,
|
|
184
|
+
targetKind: "project",
|
|
185
|
+
content,
|
|
186
|
+
now,
|
|
187
|
+
});
|
|
188
|
+
if (result !== null) {
|
|
189
|
+
db.prepare("UPDATE platform_clarification_projection_cursors SET version = ?, closed = ? WHERE round_id = ?").run(content.version, content.state === "closed" ? 1 : 0, cursor.round_id);
|
|
190
|
+
if (result.created)
|
|
191
|
+
created.push(result.intent);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
165
194
|
return created;
|
|
166
195
|
})();
|
|
167
196
|
}
|
|
197
|
+
function clarificationContent(db, roundId) {
|
|
198
|
+
const round = readClarificationRoundProjection(db, roundId);
|
|
199
|
+
if (round === null)
|
|
200
|
+
return null;
|
|
201
|
+
const thread = readClarificationThreadResponse(db, round.thread_id)?.thread;
|
|
202
|
+
const state = thread?.status !== "active" || thread.current_round_id !== round.id
|
|
203
|
+
? "closed"
|
|
204
|
+
: round.successor_status === "materialized"
|
|
205
|
+
? "continued"
|
|
206
|
+
: round.status === "sealed"
|
|
207
|
+
? "submitted"
|
|
208
|
+
: "writable";
|
|
209
|
+
const answerRevision = round.answer_revision ?? 0;
|
|
210
|
+
return {
|
|
211
|
+
kind: "clarification",
|
|
212
|
+
round_id: round.id,
|
|
213
|
+
answer_revision: answerRevision,
|
|
214
|
+
version: answerRevision * 4 + { writable: 0, submitted: 1, continued: 2, closed: 3 }[state],
|
|
215
|
+
state,
|
|
216
|
+
...(state === "closed" && thread?.closed_reason !== undefined
|
|
217
|
+
? { closed_reason: thread.closed_reason }
|
|
218
|
+
: {}),
|
|
219
|
+
title: (round.request_payload.title ?? "Needs your input").slice(0, 512),
|
|
220
|
+
request: round.request_payload.request.slice(0, 4_000),
|
|
221
|
+
opened_at: round.created_at,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
168
224
|
export function createPlatformOutboundIntent(tx, input) {
|
|
169
225
|
const existing = tx
|
|
170
226
|
.prepare("SELECT * FROM platform_outbound_intents WHERE canonical_message_id = ?")
|
|
@@ -172,13 +228,27 @@ export function createPlatformOutboundIntent(tx, input) {
|
|
|
172
228
|
if (existing !== undefined) {
|
|
173
229
|
return { created: false, intent: existing };
|
|
174
230
|
}
|
|
231
|
+
// An H5 round answer can finish before the periodic status worker observes the
|
|
232
|
+
// opening card. Capture its destination first, in this same transaction.
|
|
233
|
+
if (tx
|
|
234
|
+
.prepare("SELECT 1 FROM messages WHERE id = ? AND scope_kind = 'clarification_round'")
|
|
235
|
+
.get(input.triggerMessageId) !== undefined)
|
|
236
|
+
materializePlatformStatusOutboundIntents(tx, input.now);
|
|
175
237
|
const destination = tx
|
|
176
238
|
.prepare(`
|
|
177
239
|
SELECT platform, installation_ref, binding_ref
|
|
178
240
|
FROM external_source_mappings
|
|
179
241
|
WHERE message_id = ? AND platform = 'feishu'
|
|
180
242
|
`)
|
|
181
|
-
.get(input.triggerMessageId)
|
|
243
|
+
.get(input.triggerMessageId) ??
|
|
244
|
+
tx
|
|
245
|
+
.prepare(`
|
|
246
|
+
SELECT i.platform, i.installation_ref, i.binding_ref FROM messages m
|
|
247
|
+
JOIN platform_clarification_projection_cursors c ON c.round_id = m.clarification_round_id
|
|
248
|
+
JOIN platform_outbound_intents i ON i.event_key = 'clarification_round:' || c.round_id || ':opened'
|
|
249
|
+
WHERE m.id = ? AND m.scope_kind = 'clarification_round'
|
|
250
|
+
`)
|
|
251
|
+
.get(input.triggerMessageId);
|
|
182
252
|
if (destination === undefined) {
|
|
183
253
|
return null;
|
|
184
254
|
}
|
|
@@ -216,7 +286,7 @@ export function claimDuePlatformOutboundIntent(db, now = () => new Date()) {
|
|
|
216
286
|
const row = db
|
|
217
287
|
.prepare(`
|
|
218
288
|
SELECT platform_outbound_intents.*,
|
|
219
|
-
COALESCE(platform_outbound_intents.content_text, messages.body) AS body
|
|
289
|
+
COALESCE(platform_outbound_intents.content_text, messages.body) AS body, messages.clarification_round_id
|
|
220
290
|
FROM platform_outbound_intents
|
|
221
291
|
LEFT JOIN messages ON messages.id = platform_outbound_intents.canonical_message_id
|
|
222
292
|
WHERE platform_outbound_intents.disposition IN ('pending', 'retry_wait')
|
|
@@ -243,7 +313,16 @@ export function claimDuePlatformOutboundIntent(db, now = () => new Date()) {
|
|
|
243
313
|
}
|
|
244
314
|
else if (row.body !== null) {
|
|
245
315
|
const body = boundedPlatformOutboundText(row.body);
|
|
246
|
-
content =
|
|
316
|
+
content =
|
|
317
|
+
body.length === 0
|
|
318
|
+
? null
|
|
319
|
+
: {
|
|
320
|
+
kind: "text",
|
|
321
|
+
text: body,
|
|
322
|
+
...(row.clarification_round_id == null
|
|
323
|
+
? {}
|
|
324
|
+
: { clarification_round_ref: row.clarification_round_id }),
|
|
325
|
+
};
|
|
247
326
|
}
|
|
248
327
|
if (content === null) {
|
|
249
328
|
return null;
|
|
@@ -311,6 +311,7 @@ export async function startLaunchProject(options) {
|
|
|
311
311
|
projectSurface: preparation.machine_local.binding.surface_profile.kind,
|
|
312
312
|
trustedMetadataStore: host.trusted_relay_metadata_store,
|
|
313
313
|
integrationDelivery: host.integration_delivery,
|
|
314
|
+
integrationClarification: host.integration_clarification,
|
|
314
315
|
integrationReferenceAttachment: host.integration_reference_attachment,
|
|
315
316
|
integrationManagedReference: host.integration_managed_reference,
|
|
316
317
|
integrationApprovalDecision: host.integration_approval_decision,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FastifyInstance } from "fastify";
|
|
2
2
|
import { type ExecutionStatusProjection } from "@tutti/shared/schemas/api";
|
|
3
|
-
import type { IntegrationApprovalDecisionDisposition, IntegrationDeliveryDisposition, IntegrationManagedReferenceDisposition, TrustedApprovalDecisionCommand, TrustedConversationSourceIngestionCommand, TrustedManagedReferenceIngestionCommand, TrustedReferenceAttachmentIngestionCommand } from "@tutti/shared/schemas/internal";
|
|
3
|
+
import type { IntegrationApprovalDecisionDisposition, IntegrationClarificationResponse, TrustedClarificationCommand, IntegrationDeliveryDisposition, IntegrationManagedReferenceDisposition, TrustedApprovalDecisionCommand, TrustedConversationSourceIngestionCommand, TrustedManagedReferenceIngestionCommand, TrustedReferenceAttachmentIngestionCommand } from "@tutti/shared/schemas/internal";
|
|
4
4
|
import { type HostProjectStore } from "../../store/index.js";
|
|
5
5
|
import type { createHostServer } from "../http/create-server.js";
|
|
6
6
|
import type { HostLocalLaunchStatus } from "../http/routes/local-control.js";
|
|
@@ -19,6 +19,7 @@ export type HostServerHandle = {
|
|
|
19
19
|
runtime_endpoint: MachineRuntimeEndpointRecord;
|
|
20
20
|
trusted_relay_metadata_store: TrustedRelaySessionMetadataStore;
|
|
21
21
|
integration_delivery: (command: TrustedConversationSourceIngestionCommand) => IntegrationDeliveryDisposition;
|
|
22
|
+
integration_clarification: (command: TrustedClarificationCommand) => IntegrationClarificationResponse;
|
|
22
23
|
integration_approval_decision: (command: TrustedApprovalDecisionCommand) => IntegrationApprovalDecisionDisposition;
|
|
23
24
|
integration_reference_attachment: (command: TrustedReferenceAttachmentIngestionCommand) => Promise<IntegrationDeliveryDisposition>;
|
|
24
25
|
integration_managed_reference: (command: TrustedManagedReferenceIngestionCommand) => Promise<IntegrationManagedReferenceDisposition>;
|
|
@@ -6,7 +6,7 @@ import { ApprovalRequestManager, executeApprovalDecisionCommand } from "../../ap
|
|
|
6
6
|
import { ArtifactPreviewManager } from "../../artifacts/index.js";
|
|
7
7
|
import { ReadOnlyChatAssistant } from "../../chat-assistant/index.js";
|
|
8
8
|
import { CheckProcessCoordinator } from "../../checks/index.js";
|
|
9
|
-
import { ingestTrustedConversationSource, ingestTrustedManagedReference, ingestTrustedReferenceAttachment, } from "../../collaboration-ingestion/index.js";
|
|
9
|
+
import { ingestTrustedConversationSource, executeTrustedClarification, ingestTrustedManagedReference, ingestTrustedReferenceAttachment, } from "../../collaboration-ingestion/index.js";
|
|
10
10
|
import { ensureInitialProjectWelcomeMessage } from "../../collaboration-state/index.js";
|
|
11
11
|
import { Phase5ControlPlane } from "../../control-plane/index.js";
|
|
12
12
|
import { resolveProjectOpenAiProcedureWorkflowRunner } from "../../control-plane/workflows/index.js";
|
|
@@ -447,7 +447,7 @@ export async function startForegroundHostServer(options) {
|
|
|
447
447
|
...(platformOutboundCoordinator === undefined
|
|
448
448
|
? {}
|
|
449
449
|
: {
|
|
450
|
-
|
|
450
|
+
onAgentMessageCreated: ({ tx, triggerMessage, responseMessage }) => platformOutboundCoordinator.captureAgentResponse(tx, {
|
|
451
451
|
triggerMessage,
|
|
452
452
|
responseMessage,
|
|
453
453
|
}),
|
|
@@ -714,6 +714,7 @@ export async function startForegroundHostServer(options) {
|
|
|
714
714
|
trusted_relay_metadata_store: trustedMetadataStore,
|
|
715
715
|
integration_delivery: (command) => ingestTrustedConversationSource({
|
|
716
716
|
store,
|
|
717
|
+
controlPlane,
|
|
717
718
|
events: workspaceEvents,
|
|
718
719
|
command,
|
|
719
720
|
scratchpadSource: {
|
|
@@ -722,6 +723,7 @@ export async function startForegroundHostServer(options) {
|
|
|
722
723
|
chatAssistant,
|
|
723
724
|
now,
|
|
724
725
|
}),
|
|
726
|
+
integration_clarification: (command) => executeTrustedClarification({ store, controlPlane, command, now }),
|
|
725
727
|
integration_reference_attachment: async (command) => await ingestTrustedReferenceAttachment({
|
|
726
728
|
store,
|
|
727
729
|
workspaceRoot: options.preparation.workspace_root,
|
|
@@ -38,6 +38,7 @@ export async function registerHostWithRelay(options) {
|
|
|
38
38
|
capabilities: {
|
|
39
39
|
integration_source_ingestion_v1: true,
|
|
40
40
|
integration_development_bot_v1: true,
|
|
41
|
+
integration_clarification_v1: true,
|
|
41
42
|
integration_external_references_v1: true,
|
|
42
43
|
integration_attachment_ingestion_v1: true,
|
|
43
44
|
integration_managed_reference_ingestion_v1: true,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { FastifyInstance } from "fastify";
|
|
2
2
|
import type { RelayHostTunnelRequestHandler, RelayHostTunnelStreamRequestHandler } from "@tutti/relay-client";
|
|
3
|
-
import type { IntegrationApprovalDecisionDisposition, IntegrationDeliveryDisposition, IntegrationManagedReferenceDisposition, TrustedApprovalDecisionCommand, TrustedConversationSourceIngestionCommand, TrustedManagedReferenceIngestionCommand, TrustedReferenceAttachmentIngestionCommand } from "@tutti/shared/schemas/internal";
|
|
3
|
+
import type { IntegrationApprovalDecisionDisposition, IntegrationClarificationResponse, TrustedClarificationCommand, IntegrationDeliveryDisposition, IntegrationManagedReferenceDisposition, TrustedApprovalDecisionCommand, TrustedConversationSourceIngestionCommand, TrustedManagedReferenceIngestionCommand, TrustedReferenceAttachmentIngestionCommand } from "@tutti/shared/schemas/internal";
|
|
4
4
|
import type { ProjectSurface } from "@tutti/shared/domain";
|
|
5
5
|
import { type TrustedRelaySessionMetadataStore } from "../session/relay-session-context.js";
|
|
6
6
|
export type HostProjectApiTunnelDispatchOptions = {
|
|
7
7
|
app: FastifyInstance;
|
|
8
8
|
projectSurface?: ProjectSurface;
|
|
9
9
|
trustedMetadataStore: TrustedRelaySessionMetadataStore;
|
|
10
|
+
integrationClarification?: (command: TrustedClarificationCommand) => IntegrationClarificationResponse;
|
|
10
11
|
integrationDelivery?: (command: TrustedConversationSourceIngestionCommand) => IntegrationDeliveryDisposition;
|
|
11
12
|
integrationApprovalDecision?: (command: TrustedApprovalDecisionCommand) => IntegrationApprovalDecisionDisposition;
|
|
12
13
|
integrationReferenceAttachment?: (command: TrustedReferenceAttachmentIngestionCommand) => Promise<IntegrationDeliveryDisposition>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dispatchIntegrationClarification } from "./integration-clarification-dispatch.js";
|
|
2
|
+
import { INTEGRATION_APPROVAL_DECISION_TUNNEL_PATH, INTEGRATION_CLARIFICATION_TUNNEL_PATH, INTEGRATION_MANAGED_REFERENCE_TUNNEL_PATH, INTEGRATION_REFERENCE_ATTACHMENT_TUNNEL_PATH, isTrustedApprovalDecisionCommand, isTrustedConversationSourceIngestionCommand, isTrustedManagedReferenceIngestionCommand, isTrustedReferenceAttachmentIngestionCommand, } from "@tutti/shared/schemas/internal";
|
|
2
3
|
import { TRUSTED_INTEGRATION_INGESTION_PATH, TRUSTED_INTEGRATION_REFERENCE_ATTACHMENT_PATH, } from "../../collaboration-ingestion/index.js";
|
|
3
4
|
import { TRUSTED_RELAY_METADATA_REQUEST_HEADER, } from "../session/relay-session-context.js";
|
|
4
5
|
function browserMetadataMatchesSurface(projectSurface, request) {
|
|
@@ -78,6 +79,8 @@ function dispatchIntegrationDelivery(options, request) {
|
|
|
78
79
|
if (request.path === INTEGRATION_APPROVAL_DECISION_TUNNEL_PATH) {
|
|
79
80
|
return dispatchIntegrationApprovalDecision(options, request);
|
|
80
81
|
}
|
|
82
|
+
if (request.path === INTEGRATION_CLARIFICATION_TUNNEL_PATH)
|
|
83
|
+
return Promise.resolve(dispatchIntegrationClarification(options, request));
|
|
81
84
|
if (request.path === INTEGRATION_REFERENCE_ATTACHMENT_TUNNEL_PATH) {
|
|
82
85
|
return dispatchIntegrationReferenceAttachment(options, request);
|
|
83
86
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RelayHostTunnelRequest, RelayHostTunnelResponse } from "@tutti/relay-client";
|
|
2
|
+
import { type IntegrationClarificationResponse, type TrustedClarificationCommand } from "@tutti/shared/schemas/internal";
|
|
3
|
+
export declare function dispatchIntegrationClarification(options: {
|
|
4
|
+
integrationClarification?: (command: TrustedClarificationCommand) => IntegrationClarificationResponse;
|
|
5
|
+
}, request: RelayHostTunnelRequest): RelayHostTunnelResponse;
|
|
6
|
+
//# sourceMappingURL=integration-clarification-dispatch.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { INTEGRATION_CLARIFICATION_TUNNEL_PATH, isTrustedClarificationCommand, } from "@tutti/shared/schemas/internal";
|
|
2
|
+
export function dispatchIntegrationClarification(options, request) {
|
|
3
|
+
const finish = (body) => ({
|
|
4
|
+
request_id: request.request_id,
|
|
5
|
+
status_code: 200,
|
|
6
|
+
headers: { "content-type": "application/json; charset=utf-8" },
|
|
7
|
+
body_base64: Buffer.from(JSON.stringify(body), "utf8").toString("base64"),
|
|
8
|
+
});
|
|
9
|
+
const invalid = (reason_code) => finish({ protocol_version: 1, disposition: { kind: "invalid", reason_code } });
|
|
10
|
+
if (request.method !== "POST" ||
|
|
11
|
+
request.path !== INTEGRATION_CLARIFICATION_TUNNEL_PATH ||
|
|
12
|
+
!("integration_delivery" in request.metadata))
|
|
13
|
+
return invalid("internal_route_invalid");
|
|
14
|
+
let command;
|
|
15
|
+
try {
|
|
16
|
+
command = JSON.parse(Buffer.from(request.body_base64 ?? "", "base64").toString("utf8"));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return invalid("trusted_command_invalid");
|
|
20
|
+
}
|
|
21
|
+
if (!isTrustedClarificationCommand(command))
|
|
22
|
+
return invalid("trusted_command_invalid");
|
|
23
|
+
const metadata = request.metadata.integration_delivery;
|
|
24
|
+
if (!("operation_ref" in metadata) ||
|
|
25
|
+
command.operation_ref !== metadata.operation_ref ||
|
|
26
|
+
command.binding_ref !== metadata.binding_ref ||
|
|
27
|
+
command.relay_project_ref !== metadata.relay_project_ref ||
|
|
28
|
+
command.actor.verified_at !== metadata.verified_at ||
|
|
29
|
+
command.conversation.installation.platform !== metadata.installation.platform ||
|
|
30
|
+
command.conversation.installation.installation_ref !== metadata.installation.installation_ref)
|
|
31
|
+
return invalid("trusted_metadata_mismatch");
|
|
32
|
+
if (options.integrationClarification === undefined)
|
|
33
|
+
return finish({
|
|
34
|
+
protocol_version: 1,
|
|
35
|
+
disposition: {
|
|
36
|
+
kind: "unavailable",
|
|
37
|
+
retryable: true,
|
|
38
|
+
reason_code: "integration_clarification_unsupported",
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
return finish(options.integrationClarification(command));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=integration-clarification-dispatch.js.map
|