@markusylisiurunen/tau 0.3.42 → 0.3.44
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 +9 -11
- package/dist/core/agent/agent_runtime.js +55 -52
- package/dist/core/agent/agent_runtime.js.map +1 -1
- package/dist/core/config/builtin_themes.js +62 -155
- package/dist/core/config/builtin_themes.js.map +1 -1
- package/dist/core/config/content_loader.js +1 -63
- package/dist/core/config/content_loader.js.map +1 -1
- package/dist/core/runtime/chat_runtime.js +4 -4
- package/dist/core/session/runner.js +21 -14
- package/dist/core/session/runner.js.map +1 -1
- package/dist/core/static/code_mode/capabilities.mjs +25 -0
- package/dist/core/static/code_mode/history/documentation.md +2 -0
- package/dist/core/static/code_mode/history/sandbox_runner.mjs +4 -1
- package/dist/core/static/code_mode/nook/documentation.md +2 -0
- package/dist/core/static/code_mode/nook/sandbox_runner.mjs +4 -1
- package/dist/core/static/code_mode/web/documentation.md +2 -0
- package/dist/core/static/code_mode/web/sandbox_runner.mjs +4 -1
- package/dist/core/subagents/agent_supervisor.js +4 -4
- package/dist/core/subagents/agent_supervisor.js.map +1 -1
- package/dist/core/subagents/registry.js +5 -9
- package/dist/core/subagents/registry.js.map +1 -1
- package/dist/core/subagents/types.js.map +1 -1
- package/dist/core/telegram/adapter.js +0 -23
- package/dist/core/telegram/adapter.js.map +1 -1
- package/dist/core/telegram/session_manager.js +34 -7
- package/dist/core/telegram/session_manager.js.map +1 -1
- package/dist/core/tools/bash.js +9 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/catalog.js +2 -2
- package/dist/core/tools/code_mode.js +1 -0
- package/dist/core/tools/code_mode.js.map +1 -1
- package/dist/core/tools/spawn_agent.js +30 -40
- package/dist/core/tools/spawn_agent.js.map +1 -1
- package/dist/core/utils/auto_retry.js +1 -1
- package/dist/core/utils/auto_retry.js.map +1 -1
- package/dist/core/version.js +1 -1
- package/dist/host/execution_runtime.js +8 -18
- package/dist/host/execution_runtime.js.map +1 -1
- package/dist/host/local_session_host.js +446 -216
- package/dist/host/local_session_host.js.map +1 -1
- package/dist/host/session_host.js.map +1 -1
- package/dist/host/session_protocol_handler.js +3 -8
- package/dist/host/session_protocol_handler.js.map +1 -1
- package/dist/protocol/index.d.ts +2 -2
- package/dist/protocol/index.js +1 -1
- package/dist/protocol/index.js.map +1 -1
- package/dist/protocol/session_protocol.d.ts +127 -40
- package/dist/protocol/session_protocol.js +431 -98
- package/dist/protocol/session_protocol.js.map +1 -1
- package/dist/sdk/index.d.ts +4 -3
- package/dist/sdk/index.js +9 -1
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/types.d.ts +2 -1
- package/dist/store/session_snapshot_migrations.js +331 -4
- package/dist/store/session_snapshot_migrations.js.map +1 -1
- package/dist/tui/chat_controller/assistant_clipboard.js +12 -8
- package/dist/tui/chat_controller/assistant_clipboard.js.map +1 -1
- package/dist/tui/chat_controller/diff_review_service.js +13 -9
- package/dist/tui/chat_controller/diff_review_service.js.map +1 -1
- package/dist/tui/chat_controller/history_message_model.js +3 -3
- package/dist/tui/chat_controller/history_message_model.js.map +1 -1
- package/dist/tui/chat_view.js +35 -35
- package/dist/tui/chat_view.js.map +1 -1
- package/dist/tui/session_chat_app.js +1 -3
- package/dist/tui/session_chat_app.js.map +1 -1
- package/dist/tui/session_chat_controller.js +373 -239
- package/dist/tui/session_chat_controller.js.map +1 -1
- package/dist/tui/speech_playback.js +4 -4
- package/dist/tui/speech_playback.js.map +1 -1
- package/dist/tui/ui/assistant_message.js +1 -15
- package/dist/tui/ui/assistant_message.js.map +1 -1
- package/dist/tui/ui/chat_message_model.js +26 -7
- package/dist/tui/ui/chat_message_model.js.map +1 -1
- package/dist/tui/ui/footer.js +100 -66
- package/dist/tui/ui/footer.js.map +1 -1
- package/dist/tui/ui/theme/palette.js +10 -16
- package/dist/tui/ui/theme/palette.js.map +1 -1
- package/dist/tui/ui/theme/theme.js.map +1 -1
- package/dist/tui/ui/tool_card.js +1 -2
- package/dist/tui/ui/tool_card.js.map +1 -1
- package/dist/tui/ui/transcript_notice.js +44 -0
- package/dist/tui/ui/transcript_notice.js.map +1 -0
- package/dist/tui/ui/transcript_text.js +14 -0
- package/dist/tui/ui/transcript_text.js.map +1 -0
- package/package.json +5 -7
- package/dist/sdk/turn_outcome.d.ts +0 -2
- package/dist/sdk/turn_outcome.js +0 -4
- package/dist/sdk/turn_outcome.js.map +0 -1
- package/dist/tui/ui/system_message.js +0 -22
- package/dist/tui/ui/system_message.js.map +0 -1
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export const SESSION_PROTOCOL_VERSION =
|
|
2
|
+
export const SESSION_PROTOCOL_VERSION = 10;
|
|
3
3
|
export const SESSION_PROTOCOL_MAX_EXEC_CAPTURE_BYTES = 24 * 1024 * 1024;
|
|
4
4
|
export const SESSION_PROTOCOL_MAX_EXEC_STDIN_BYTES = 16 * 1024 * 1024;
|
|
5
|
+
const SESSION_PROTOCOL_NOTICE_TITLE_MAX_CHARS = 512;
|
|
6
|
+
const SESSION_PROTOCOL_NOTICE_CONTENT_MAX_ENTRIES = 16;
|
|
7
|
+
const SESSION_PROTOCOL_NOTICE_CONTENT_ENTRY_MAX_CHARS = 4_096;
|
|
5
8
|
export const SESSION_PROTOCOL_METHODS = [
|
|
6
9
|
"initialize",
|
|
7
10
|
"session.create",
|
|
@@ -65,6 +68,22 @@ const SESSION_PROTOCOL_ERROR_CODE_VALUES = [
|
|
|
65
68
|
SESSION_PROTOCOL_ERROR_CODES.cancelled,
|
|
66
69
|
SESSION_PROTOCOL_ERROR_CODES.internalError,
|
|
67
70
|
];
|
|
71
|
+
export function projectSessionProtocolNoticePresentation(title, content) {
|
|
72
|
+
const projectedContent = content
|
|
73
|
+
? content
|
|
74
|
+
.slice(0, SESSION_PROTOCOL_NOTICE_CONTENT_MAX_ENTRIES)
|
|
75
|
+
.map((entry) => truncateNoticeText(entry, SESSION_PROTOCOL_NOTICE_CONTENT_ENTRY_MAX_CHARS))
|
|
76
|
+
: [];
|
|
77
|
+
return {
|
|
78
|
+
title: truncateNoticeText(title, SESSION_PROTOCOL_NOTICE_TITLE_MAX_CHARS),
|
|
79
|
+
...(projectedContent.length > 0 ? { content: projectedContent } : {}),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function truncateNoticeText(value, maxChars) {
|
|
83
|
+
if (value.length <= maxChars)
|
|
84
|
+
return value;
|
|
85
|
+
return `${value.slice(0, maxChars - 1)}…`;
|
|
86
|
+
}
|
|
68
87
|
const sessionProtocolMethodSchema = z.enum(SESSION_PROTOCOL_METHODS);
|
|
69
88
|
const sessionProtocolMethodListSchema = z
|
|
70
89
|
.array(sessionProtocolMethodSchema)
|
|
@@ -86,6 +105,10 @@ const nonEmptyStringSchema = z
|
|
|
86
105
|
.string()
|
|
87
106
|
.min(1)
|
|
88
107
|
.refine((value) => value.trim().length > 0);
|
|
108
|
+
const historyEntryIdSchema = nonEmptyStringSchema.transform((value) => value.trim());
|
|
109
|
+
const noticeTitleSchema = nonEmptyStringSchema
|
|
110
|
+
.max(SESSION_PROTOCOL_NOTICE_TITLE_MAX_CHARS)
|
|
111
|
+
.refine((value) => !value.includes("\n") && !value.includes("\r"), "notice title must be one line");
|
|
89
112
|
const sessionProtocolRequestIdSchema = nonEmptyStringSchema;
|
|
90
113
|
const nullableSessionProtocolRequestIdSchema = sessionProtocolRequestIdSchema.nullable();
|
|
91
114
|
const absolutePathSchema = nonEmptyStringSchema.refine((value) => value.startsWith("/"));
|
|
@@ -348,7 +371,7 @@ const sessionProtocolUserMessageParamsSchema = z
|
|
|
348
371
|
.object({
|
|
349
372
|
sessionId: nonEmptyStringSchema,
|
|
350
373
|
text: nonEmptyStringSchema,
|
|
351
|
-
historyEntryId:
|
|
374
|
+
historyEntryId: historyEntryIdSchema.optional(),
|
|
352
375
|
})
|
|
353
376
|
.strip();
|
|
354
377
|
const sessionProtocolStartGoalParamsSchema = z
|
|
@@ -462,7 +485,7 @@ const sessionProtocolCompactParamsSchema = z
|
|
|
462
485
|
const sessionProtocolRewindParamsSchema = z
|
|
463
486
|
.object({
|
|
464
487
|
sessionId: nonEmptyStringSchema,
|
|
465
|
-
historyEntryId:
|
|
488
|
+
historyEntryId: historyEntryIdSchema,
|
|
466
489
|
})
|
|
467
490
|
.strip();
|
|
468
491
|
const sessionProtocolInterruptSubagentParamsSchema = z
|
|
@@ -753,50 +776,136 @@ const sessionProtocolMessageSchema = z
|
|
|
753
776
|
state: z.enum(["draft", "committed", "interrupted", "discarded"]),
|
|
754
777
|
modelVisible: z.boolean(),
|
|
755
778
|
message: sessionProtocolMessagePayloadSchema,
|
|
756
|
-
turn: sessionProtocolTurnOutcomeSchema.optional(),
|
|
757
779
|
})
|
|
758
780
|
.strip();
|
|
781
|
+
const sessionProtocolSubjectSchema = z.discriminatedUnion("type", [
|
|
782
|
+
z.object({ type: z.literal("session") }).strip(),
|
|
783
|
+
z.object({ type: z.literal("message"), id: nonEmptyStringSchema }).strip(),
|
|
784
|
+
z.object({ type: z.literal("tool"), id: nonEmptyStringSchema }).strip(),
|
|
785
|
+
z.object({ type: z.literal("agent"), id: nonEmptyStringSchema }).strip(),
|
|
786
|
+
z.object({ type: z.literal("operation"), id: nonEmptyStringSchema }).strip(),
|
|
787
|
+
]);
|
|
788
|
+
const timelineNoticeKindSchema = nonEmptyStringSchema.regex(/^[a-z0-9-]+(?:\.[a-z0-9-]+)+$/, "timeline notice kind must be a lowercase dotted identifier");
|
|
759
789
|
const sessionProtocolNoticeSchema = z
|
|
760
790
|
.object({
|
|
791
|
+
kind: timelineNoticeKindSchema,
|
|
792
|
+
version: z.number().int().positive(),
|
|
761
793
|
severity: z.enum(["info", "warn", "error"]),
|
|
762
|
-
|
|
763
|
-
|
|
794
|
+
subject: sessionProtocolSubjectSchema,
|
|
795
|
+
presentation: z
|
|
796
|
+
.object({
|
|
797
|
+
title: noticeTitleSchema,
|
|
798
|
+
content: z
|
|
799
|
+
.array(z.string().max(SESSION_PROTOCOL_NOTICE_CONTENT_ENTRY_MAX_CHARS))
|
|
800
|
+
.min(1)
|
|
801
|
+
.max(SESSION_PROTOCOL_NOTICE_CONTENT_MAX_ENTRIES)
|
|
802
|
+
.optional(),
|
|
803
|
+
})
|
|
804
|
+
.strip(),
|
|
805
|
+
data: z.record(z.string(), z.unknown()),
|
|
764
806
|
})
|
|
765
807
|
.strip();
|
|
766
|
-
const
|
|
767
|
-
|
|
808
|
+
const sessionProtocolOperationBaseSchema = {
|
|
809
|
+
id: nonEmptyStringSchema,
|
|
768
810
|
kind: z.enum(["auto-compaction", "manual-compaction", "reload", "rewind"]),
|
|
769
|
-
status: z.enum(["running", "succeeded", "failed", "cancelled", "skipped"]),
|
|
770
811
|
startedAt: z.number().finite(),
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
812
|
+
};
|
|
813
|
+
const sessionProtocolOperationSchema = z.discriminatedUnion("status", [
|
|
814
|
+
z.object({ ...sessionProtocolOperationBaseSchema, status: z.literal("running") }).strip(),
|
|
815
|
+
z
|
|
816
|
+
.object({
|
|
817
|
+
...sessionProtocolOperationBaseSchema,
|
|
818
|
+
status: z.literal("succeeded"),
|
|
819
|
+
finishedAt: z.number().finite(),
|
|
820
|
+
})
|
|
821
|
+
.strip(),
|
|
822
|
+
z
|
|
823
|
+
.object({
|
|
824
|
+
...sessionProtocolOperationBaseSchema,
|
|
825
|
+
status: z.literal("failed"),
|
|
826
|
+
finishedAt: z.number().finite(),
|
|
827
|
+
error: nonEmptyStringSchema,
|
|
828
|
+
})
|
|
829
|
+
.strip(),
|
|
830
|
+
z
|
|
831
|
+
.object({
|
|
832
|
+
...sessionProtocolOperationBaseSchema,
|
|
833
|
+
status: z.literal("skipped"),
|
|
834
|
+
finishedAt: z.number().finite(),
|
|
835
|
+
reason: z.literal("no-eligible-history"),
|
|
836
|
+
})
|
|
837
|
+
.strip(),
|
|
838
|
+
z
|
|
839
|
+
.object({
|
|
840
|
+
...sessionProtocolOperationBaseSchema,
|
|
841
|
+
status: z.literal("cancelled"),
|
|
842
|
+
finishedAt: z.number().finite(),
|
|
843
|
+
reason: z.enum(["interrupted", "session-recovered"]),
|
|
844
|
+
})
|
|
845
|
+
.strip(),
|
|
846
|
+
]);
|
|
847
|
+
const sessionProtocolTimelineItemBaseSchema = {
|
|
848
|
+
id: nonEmptyStringSchema,
|
|
849
|
+
sequence: z.number().int().positive(),
|
|
850
|
+
createdAt: z.number().finite().nonnegative(),
|
|
851
|
+
};
|
|
777
852
|
const sessionProtocolTimelineItemSchema = z.discriminatedUnion("type", [
|
|
778
853
|
z
|
|
779
854
|
.object({
|
|
855
|
+
...sessionProtocolTimelineItemBaseSchema,
|
|
780
856
|
type: z.literal("message"),
|
|
781
|
-
id: nonEmptyStringSchema,
|
|
782
857
|
messageId: nonEmptyStringSchema,
|
|
783
858
|
})
|
|
784
859
|
.strip(),
|
|
785
860
|
z
|
|
786
861
|
.object({
|
|
862
|
+
...sessionProtocolTimelineItemBaseSchema,
|
|
863
|
+
type: z.literal("tool"),
|
|
864
|
+
toolId: nonEmptyStringSchema,
|
|
865
|
+
})
|
|
866
|
+
.strip(),
|
|
867
|
+
z
|
|
868
|
+
.object({
|
|
869
|
+
...sessionProtocolTimelineItemBaseSchema,
|
|
787
870
|
type: z.literal("notice"),
|
|
788
|
-
id: nonEmptyStringSchema,
|
|
789
871
|
notice: sessionProtocolNoticeSchema,
|
|
790
872
|
})
|
|
791
873
|
.strip(),
|
|
792
874
|
z
|
|
793
875
|
.object({
|
|
876
|
+
...sessionProtocolTimelineItemBaseSchema,
|
|
794
877
|
type: z.literal("operation"),
|
|
795
|
-
|
|
796
|
-
operation: sessionProtocolOperationSchema,
|
|
878
|
+
operationId: nonEmptyStringSchema,
|
|
797
879
|
})
|
|
798
880
|
.strip(),
|
|
799
881
|
]);
|
|
882
|
+
const sessionProtocolTimelineSchema = z
|
|
883
|
+
.object({
|
|
884
|
+
epoch: z.number().int().positive(),
|
|
885
|
+
sequence: z.number().int().nonnegative(),
|
|
886
|
+
items: z.array(sessionProtocolTimelineItemSchema),
|
|
887
|
+
})
|
|
888
|
+
.strip()
|
|
889
|
+
.superRefine((timeline, ctx) => {
|
|
890
|
+
let previousSequence = 0;
|
|
891
|
+
for (const [index, item] of timeline.items.entries()) {
|
|
892
|
+
if (item.sequence <= previousSequence) {
|
|
893
|
+
ctx.addIssue({
|
|
894
|
+
code: "custom",
|
|
895
|
+
path: ["items", index, "sequence"],
|
|
896
|
+
message: "timeline item sequences must be strictly increasing",
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
if (item.sequence > timeline.sequence) {
|
|
900
|
+
ctx.addIssue({
|
|
901
|
+
code: "custom",
|
|
902
|
+
path: ["items", index, "sequence"],
|
|
903
|
+
message: "timeline item sequence exceeds the timeline high-water sequence",
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
previousSequence = item.sequence;
|
|
907
|
+
}
|
|
908
|
+
});
|
|
800
909
|
const sessionProtocolToolRunBaseSchema = z.object({
|
|
801
910
|
id: nonEmptyStringSchema,
|
|
802
911
|
toolCallId: nonEmptyStringSchema,
|
|
@@ -918,17 +1027,10 @@ const sessionProtocolAgentRunSchema = z
|
|
|
918
1027
|
});
|
|
919
1028
|
}
|
|
920
1029
|
});
|
|
921
|
-
const sessionProtocolFacetSubjectSchema = z.discriminatedUnion("type", [
|
|
922
|
-
z.object({ type: z.literal("session") }).strip(),
|
|
923
|
-
z.object({ type: z.literal("message"), id: nonEmptyStringSchema }).strip(),
|
|
924
|
-
z.object({ type: z.literal("tool"), id: nonEmptyStringSchema }).strip(),
|
|
925
|
-
z.object({ type: z.literal("agent"), id: nonEmptyStringSchema }).strip(),
|
|
926
|
-
z.object({ type: z.literal("operation"), id: nonEmptyStringSchema }).strip(),
|
|
927
|
-
]);
|
|
928
1030
|
const sessionProtocolFacetSchema = z
|
|
929
1031
|
.object({
|
|
930
1032
|
id: nonEmptyStringSchema,
|
|
931
|
-
subject:
|
|
1033
|
+
subject: sessionProtocolSubjectSchema,
|
|
932
1034
|
kind: nonEmptyStringSchema,
|
|
933
1035
|
version: z.number().int().positive(),
|
|
934
1036
|
data: z.record(z.string(), z.unknown()),
|
|
@@ -943,17 +1045,27 @@ const sessionProtocolGoalSchema = z
|
|
|
943
1045
|
const sessionProtocolAgentStateSnapshotSchema = z
|
|
944
1046
|
.object({
|
|
945
1047
|
revision: z.number().int().nonnegative(),
|
|
946
|
-
|
|
1048
|
+
modelContextKey: nonEmptyStringSchema,
|
|
947
1049
|
usageCheckpoint: z
|
|
948
1050
|
.object({
|
|
949
1051
|
historyEntryId: nonEmptyStringSchema,
|
|
950
|
-
|
|
1052
|
+
modelContextKey: nonEmptyStringSchema,
|
|
951
1053
|
tokens: z.number().int().nonnegative(),
|
|
952
1054
|
})
|
|
953
1055
|
.strip()
|
|
954
1056
|
.optional(),
|
|
955
1057
|
})
|
|
956
1058
|
.strip();
|
|
1059
|
+
const sessionProtocolTurnRecordSchema = z.discriminatedUnion("state", [
|
|
1060
|
+
z.object({ userHistoryEntryId: nonEmptyStringSchema, state: z.literal("running") }).strip(),
|
|
1061
|
+
z
|
|
1062
|
+
.object({
|
|
1063
|
+
userHistoryEntryId: nonEmptyStringSchema,
|
|
1064
|
+
state: z.literal("settled"),
|
|
1065
|
+
outcome: sessionProtocolTurnOutcomeSchema,
|
|
1066
|
+
})
|
|
1067
|
+
.strip(),
|
|
1068
|
+
]);
|
|
957
1069
|
const sessionProtocolSnapshotSchema = z
|
|
958
1070
|
.object({
|
|
959
1071
|
sessionId: nonEmptyStringSchema,
|
|
@@ -969,8 +1081,10 @@ const sessionProtocolSnapshotSchema = z
|
|
|
969
1081
|
catalog: sessionProtocolContentCatalogSnapshotSchema,
|
|
970
1082
|
executionEnvironment: sessionProtocolExecutionEnvironmentSnapshotSchema,
|
|
971
1083
|
messages: z.array(sessionProtocolMessageSchema),
|
|
972
|
-
|
|
1084
|
+
turns: z.record(nonEmptyStringSchema, sessionProtocolTurnRecordSchema),
|
|
1085
|
+
timeline: sessionProtocolTimelineSchema,
|
|
973
1086
|
tools: z.record(nonEmptyStringSchema, sessionProtocolToolRunSchema),
|
|
1087
|
+
operations: z.record(nonEmptyStringSchema, sessionProtocolOperationSchema),
|
|
974
1088
|
agents: z.record(nonEmptyStringSchema, sessionProtocolAgentRunSchema),
|
|
975
1089
|
facets: z.record(nonEmptyStringSchema, sessionProtocolFacetSchema),
|
|
976
1090
|
})
|
|
@@ -985,22 +1099,15 @@ const sessionProtocolSnapshotSchema = z
|
|
|
985
1099
|
message: `duplicate message id '${message.id}'`,
|
|
986
1100
|
});
|
|
987
1101
|
}
|
|
988
|
-
if (message.turn && message.message.role !== "user") {
|
|
989
|
-
ctx.addIssue({
|
|
990
|
-
code: "custom",
|
|
991
|
-
path: ["messages"],
|
|
992
|
-
message: `turn outcome belongs to non-user message '${message.id}'`,
|
|
993
|
-
});
|
|
994
|
-
}
|
|
995
1102
|
messagesById.set(message.id, message);
|
|
996
1103
|
}
|
|
997
1104
|
const checkpoint = snapshot.agentState.usageCheckpoint;
|
|
998
1105
|
if (checkpoint) {
|
|
999
|
-
if (checkpoint.
|
|
1106
|
+
if (checkpoint.modelContextKey !== snapshot.agentState.modelContextKey) {
|
|
1000
1107
|
ctx.addIssue({
|
|
1001
1108
|
code: "custom",
|
|
1002
|
-
path: ["agentState", "usageCheckpoint", "
|
|
1003
|
-
message: "usage checkpoint context
|
|
1109
|
+
path: ["agentState", "usageCheckpoint", "modelContextKey"],
|
|
1110
|
+
message: "usage checkpoint model context key must match agent state",
|
|
1004
1111
|
});
|
|
1005
1112
|
}
|
|
1006
1113
|
const checkpointMessage = messagesById.get(checkpoint.historyEntryId);
|
|
@@ -1019,29 +1126,109 @@ const sessionProtocolSnapshotSchema = z
|
|
|
1019
1126
|
});
|
|
1020
1127
|
}
|
|
1021
1128
|
}
|
|
1129
|
+
const subjectExists = (subject) => subject.type === "session" ||
|
|
1130
|
+
(subject.type === "message" && messagesById.has(subject.id)) ||
|
|
1131
|
+
(subject.type === "tool" && snapshot.tools[subject.id] !== undefined) ||
|
|
1132
|
+
(subject.type === "agent" && snapshot.agents[subject.id] !== undefined) ||
|
|
1133
|
+
(subject.type === "operation" && snapshot.operations[subject.id] !== undefined);
|
|
1022
1134
|
const timelineIds = new Set();
|
|
1023
|
-
const
|
|
1024
|
-
|
|
1135
|
+
const timelineSequences = new Set();
|
|
1136
|
+
const timelineToolIds = new Set();
|
|
1137
|
+
const timelineOperationIds = new Set();
|
|
1138
|
+
let previousTimelineSequence = 0;
|
|
1139
|
+
for (const item of snapshot.timeline.items) {
|
|
1025
1140
|
if (timelineIds.has(item.id)) {
|
|
1026
1141
|
ctx.addIssue({
|
|
1027
1142
|
code: "custom",
|
|
1028
|
-
path: ["timeline"],
|
|
1143
|
+
path: ["timeline", "items"],
|
|
1029
1144
|
message: `duplicate timeline item id '${item.id}'`,
|
|
1030
1145
|
});
|
|
1031
1146
|
}
|
|
1032
1147
|
timelineIds.add(item.id);
|
|
1148
|
+
if (timelineSequences.has(item.sequence) ||
|
|
1149
|
+
item.sequence <= previousTimelineSequence ||
|
|
1150
|
+
item.sequence > snapshot.timeline.sequence) {
|
|
1151
|
+
ctx.addIssue({
|
|
1152
|
+
code: "custom",
|
|
1153
|
+
path: ["timeline", "items"],
|
|
1154
|
+
message: `timeline item '${item.id}' has invalid sequence '${item.sequence}'`,
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
timelineSequences.add(item.sequence);
|
|
1158
|
+
previousTimelineSequence = item.sequence;
|
|
1033
1159
|
if (item.type === "message" && !messagesById.has(item.messageId)) {
|
|
1034
1160
|
ctx.addIssue({
|
|
1035
1161
|
code: "custom",
|
|
1036
|
-
path: ["timeline"],
|
|
1162
|
+
path: ["timeline", "items"],
|
|
1037
1163
|
message: `timeline message item '${item.id}' references unknown message '${item.messageId}'`,
|
|
1038
1164
|
});
|
|
1039
1165
|
}
|
|
1166
|
+
if (item.type === "tool") {
|
|
1167
|
+
if (timelineToolIds.has(item.toolId)) {
|
|
1168
|
+
ctx.addIssue({
|
|
1169
|
+
code: "custom",
|
|
1170
|
+
path: ["timeline", "items"],
|
|
1171
|
+
message: `tool '${item.toolId}' has multiple timeline items`,
|
|
1172
|
+
});
|
|
1173
|
+
}
|
|
1174
|
+
timelineToolIds.add(item.toolId);
|
|
1175
|
+
if (snapshot.tools[item.toolId] === undefined) {
|
|
1176
|
+
ctx.addIssue({
|
|
1177
|
+
code: "custom",
|
|
1178
|
+
path: ["timeline", "items"],
|
|
1179
|
+
message: `timeline tool item '${item.id}' references unknown tool '${item.toolId}'`,
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1040
1183
|
if (item.type === "operation") {
|
|
1041
|
-
|
|
1184
|
+
if (timelineOperationIds.has(item.operationId)) {
|
|
1185
|
+
ctx.addIssue({
|
|
1186
|
+
code: "custom",
|
|
1187
|
+
path: ["timeline", "items"],
|
|
1188
|
+
message: `operation '${item.operationId}' has multiple timeline items`,
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
timelineOperationIds.add(item.operationId);
|
|
1192
|
+
if (snapshot.operations[item.operationId] === undefined) {
|
|
1193
|
+
ctx.addIssue({
|
|
1194
|
+
code: "custom",
|
|
1195
|
+
path: ["timeline", "items"],
|
|
1196
|
+
message: `timeline operation item '${item.id}' references unknown operation '${item.operationId}'`,
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
if (item.type === "notice" && !subjectExists(item.notice.subject)) {
|
|
1201
|
+
ctx.addIssue({
|
|
1202
|
+
code: "custom",
|
|
1203
|
+
path: ["timeline", "items"],
|
|
1204
|
+
message: `timeline notice '${item.id}' references an unknown ${item.notice.subject.type} subject`,
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
for (const [id, operation] of Object.entries(snapshot.operations)) {
|
|
1209
|
+
if (!timelineOperationIds.has(id)) {
|
|
1210
|
+
ctx.addIssue({
|
|
1211
|
+
code: "custom",
|
|
1212
|
+
path: ["operations", id],
|
|
1213
|
+
message: `operation '${id}' has no timeline item`,
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
if (id !== operation.id) {
|
|
1217
|
+
ctx.addIssue({
|
|
1218
|
+
code: "custom",
|
|
1219
|
+
path: ["operations", id],
|
|
1220
|
+
message: `operation map key '${id}' does not match embedded id '${operation.id}'`,
|
|
1221
|
+
});
|
|
1042
1222
|
}
|
|
1043
1223
|
}
|
|
1044
1224
|
for (const [id, tool] of Object.entries(snapshot.tools)) {
|
|
1225
|
+
if (!timelineToolIds.has(id)) {
|
|
1226
|
+
ctx.addIssue({
|
|
1227
|
+
code: "custom",
|
|
1228
|
+
path: ["tools", id],
|
|
1229
|
+
message: `tool '${id}' has no timeline item`,
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1045
1232
|
if (id !== tool.id || id !== tool.toolCallId) {
|
|
1046
1233
|
ctx.addIssue({
|
|
1047
1234
|
code: "custom",
|
|
@@ -1119,6 +1306,15 @@ const sessionProtocolSnapshotSchema = z
|
|
|
1119
1306
|
}
|
|
1120
1307
|
}
|
|
1121
1308
|
}
|
|
1309
|
+
for (const [id, turn] of Object.entries(snapshot.turns)) {
|
|
1310
|
+
if (id !== turn.userHistoryEntryId) {
|
|
1311
|
+
ctx.addIssue({
|
|
1312
|
+
code: "custom",
|
|
1313
|
+
path: ["turns", id],
|
|
1314
|
+
message: `turn map key '${id}' does not match embedded user history entry id '${turn.userHistoryEntryId}'`,
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1122
1318
|
for (const [id, agent] of Object.entries(snapshot.agents)) {
|
|
1123
1319
|
if (id !== agent.id) {
|
|
1124
1320
|
ctx.addIssue({
|
|
@@ -1136,12 +1332,7 @@ const sessionProtocolSnapshotSchema = z
|
|
|
1136
1332
|
message: `facet map key '${id}' does not match embedded id '${facet.id}'`,
|
|
1137
1333
|
});
|
|
1138
1334
|
}
|
|
1139
|
-
|
|
1140
|
-
(facet.subject.type === "message" && messagesById.has(facet.subject.id)) ||
|
|
1141
|
-
(facet.subject.type === "tool" && snapshot.tools[facet.subject.id] !== undefined) ||
|
|
1142
|
-
(facet.subject.type === "agent" && snapshot.agents[facet.subject.id] !== undefined) ||
|
|
1143
|
-
(facet.subject.type === "operation" && operationIds.has(facet.subject.id));
|
|
1144
|
-
if (!subjectExists) {
|
|
1335
|
+
if (!subjectExists(facet.subject)) {
|
|
1145
1336
|
ctx.addIssue({
|
|
1146
1337
|
code: "custom",
|
|
1147
1338
|
path: ["facets", id, "subject"],
|
|
@@ -1150,18 +1341,41 @@ const sessionProtocolSnapshotSchema = z
|
|
|
1150
1341
|
}
|
|
1151
1342
|
}
|
|
1152
1343
|
});
|
|
1153
|
-
const
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1344
|
+
const sessionProtocolDeltaCauseSchema = z.union([
|
|
1345
|
+
z
|
|
1346
|
+
.object({
|
|
1347
|
+
type: z.enum([
|
|
1348
|
+
"user-message",
|
|
1349
|
+
"assistant-stream",
|
|
1350
|
+
"assistant-message",
|
|
1351
|
+
"tool-run",
|
|
1352
|
+
"tool-result",
|
|
1353
|
+
"notice",
|
|
1354
|
+
"agent-run",
|
|
1355
|
+
"maintenance",
|
|
1356
|
+
"configuration",
|
|
1357
|
+
"goal",
|
|
1358
|
+
]),
|
|
1359
|
+
})
|
|
1360
|
+
.strip(),
|
|
1361
|
+
z
|
|
1362
|
+
.object({
|
|
1363
|
+
type: z.literal("compaction"),
|
|
1364
|
+
previousEpoch: z.number().int().positive(),
|
|
1365
|
+
epoch: z.number().int().positive(),
|
|
1366
|
+
kind: z.enum(["auto", "manual"]),
|
|
1367
|
+
cutType: z.enum(["turn-boundary", "split-turn"]),
|
|
1368
|
+
retainedMessageCount: z.number().int().nonnegative(),
|
|
1369
|
+
})
|
|
1370
|
+
.strip(),
|
|
1371
|
+
z
|
|
1372
|
+
.object({
|
|
1373
|
+
type: z.literal("rewind"),
|
|
1374
|
+
epoch: z.number().int().positive(),
|
|
1375
|
+
cutoffSequence: z.number().int().nonnegative(),
|
|
1376
|
+
})
|
|
1377
|
+
.strip(),
|
|
1378
|
+
z.object({ type: z.literal("resync") }).strip(),
|
|
1165
1379
|
]);
|
|
1166
1380
|
const sessionProtocolChangeSchema = z.discriminatedUnion("type", [
|
|
1167
1381
|
z
|
|
@@ -1194,11 +1408,16 @@ const sessionProtocolChangeSchema = z.discriminatedUnion("type", [
|
|
|
1194
1408
|
settings: sessionProtocolSettingsSnapshotSchema,
|
|
1195
1409
|
})
|
|
1196
1410
|
.strip(),
|
|
1411
|
+
z
|
|
1412
|
+
.object({
|
|
1413
|
+
type: z.literal("turn.set"),
|
|
1414
|
+
turn: sessionProtocolTurnRecordSchema,
|
|
1415
|
+
})
|
|
1416
|
+
.strip(),
|
|
1197
1417
|
z
|
|
1198
1418
|
.object({
|
|
1199
1419
|
type: z.literal("message.append"),
|
|
1200
1420
|
message: sessionProtocolMessageSchema,
|
|
1201
|
-
timelineItem: sessionProtocolTimelineItemSchema.optional(),
|
|
1202
1421
|
})
|
|
1203
1422
|
.strip(),
|
|
1204
1423
|
z
|
|
@@ -1224,14 +1443,15 @@ const sessionProtocolChangeSchema = z.discriminatedUnion("type", [
|
|
|
1224
1443
|
.strip(),
|
|
1225
1444
|
z
|
|
1226
1445
|
.object({
|
|
1227
|
-
type: z.literal("timeline.
|
|
1228
|
-
|
|
1446
|
+
type: z.literal("timeline.remove"),
|
|
1447
|
+
id: nonEmptyStringSchema,
|
|
1229
1448
|
})
|
|
1230
1449
|
.strip(),
|
|
1231
1450
|
z
|
|
1232
1451
|
.object({
|
|
1233
|
-
type: z.literal("timeline.
|
|
1234
|
-
|
|
1452
|
+
type: z.literal("timeline.advance"),
|
|
1453
|
+
epoch: z.number().int().positive(),
|
|
1454
|
+
sequence: z.number().int().positive(),
|
|
1235
1455
|
})
|
|
1236
1456
|
.strip(),
|
|
1237
1457
|
z
|
|
@@ -1246,6 +1466,18 @@ const sessionProtocolChangeSchema = z.discriminatedUnion("type", [
|
|
|
1246
1466
|
id: nonEmptyStringSchema,
|
|
1247
1467
|
})
|
|
1248
1468
|
.strip(),
|
|
1469
|
+
z
|
|
1470
|
+
.object({
|
|
1471
|
+
type: z.literal("operation.set"),
|
|
1472
|
+
operation: sessionProtocolOperationSchema,
|
|
1473
|
+
})
|
|
1474
|
+
.strip(),
|
|
1475
|
+
z
|
|
1476
|
+
.object({
|
|
1477
|
+
type: z.literal("operation.remove"),
|
|
1478
|
+
id: nonEmptyStringSchema,
|
|
1479
|
+
})
|
|
1480
|
+
.strip(),
|
|
1249
1481
|
z
|
|
1250
1482
|
.object({
|
|
1251
1483
|
type: z.literal("agent.set"),
|
|
@@ -1292,7 +1524,7 @@ const sessionProtocolDeltaMessageSchema = z
|
|
|
1292
1524
|
sessionId: nonEmptyStringSchema,
|
|
1293
1525
|
fromRevision: z.number().int().positive().nullable(),
|
|
1294
1526
|
toRevision: z.number().int().positive(),
|
|
1295
|
-
|
|
1527
|
+
cause: sessionProtocolDeltaCauseSchema,
|
|
1296
1528
|
delta: sessionProtocolDeltaSchema,
|
|
1297
1529
|
})
|
|
1298
1530
|
.strip()
|
|
@@ -1334,6 +1566,29 @@ const sessionProtocolDeltaMessageSchema = z
|
|
|
1334
1566
|
message: "snapshot.reset revision must equal toRevision",
|
|
1335
1567
|
});
|
|
1336
1568
|
}
|
|
1569
|
+
if (message.cause.type === "compaction") {
|
|
1570
|
+
if (message.delta.type !== "snapshot.reset" ||
|
|
1571
|
+
message.cause.epoch !== message.cause.previousEpoch + 1 ||
|
|
1572
|
+
message.delta.snapshot.timeline.epoch !== message.cause.epoch) {
|
|
1573
|
+
ctx.addIssue({
|
|
1574
|
+
code: "custom",
|
|
1575
|
+
path: ["cause"],
|
|
1576
|
+
message: "compaction cause must reset the snapshot to the next timeline epoch",
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
if (message.cause.type === "rewind") {
|
|
1581
|
+
const { cutoffSequence, epoch } = message.cause;
|
|
1582
|
+
if (message.delta.type !== "snapshot.reset" ||
|
|
1583
|
+
message.delta.snapshot.timeline.epoch !== epoch ||
|
|
1584
|
+
message.delta.snapshot.timeline.items.some((item) => item.sequence > cutoffSequence)) {
|
|
1585
|
+
ctx.addIssue({
|
|
1586
|
+
code: "custom",
|
|
1587
|
+
path: ["cause"],
|
|
1588
|
+
message: "rewind cause must reset and truncate the active timeline epoch",
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1337
1592
|
});
|
|
1338
1593
|
const sessionProtocolEphemeralAgentThreadUpdateEventSchema = z
|
|
1339
1594
|
.object({
|
|
@@ -1349,7 +1604,27 @@ const sessionProtocolEphemeralAgentThreadUpdateEventSchema = z
|
|
|
1349
1604
|
.strip(),
|
|
1350
1605
|
})
|
|
1351
1606
|
.strip();
|
|
1352
|
-
const
|
|
1607
|
+
const sessionProtocolFeedbackEventSchema = z
|
|
1608
|
+
.object({
|
|
1609
|
+
type: z.literal("feedback.notice"),
|
|
1610
|
+
title: noticeTitleSchema,
|
|
1611
|
+
tone: z.enum(["default", "error"]),
|
|
1612
|
+
presentation: z.literal("footer"),
|
|
1613
|
+
durationMs: z.number().int().positive().max(60_000),
|
|
1614
|
+
})
|
|
1615
|
+
.strip();
|
|
1616
|
+
const sessionProtocolEphemeralTimelineEventSchema = z
|
|
1617
|
+
.object({
|
|
1618
|
+
type: z.literal("timeline.item"),
|
|
1619
|
+
epoch: z.number().int().positive(),
|
|
1620
|
+
item: sessionProtocolTimelineItemSchema.refine((item) => item.type === "notice", "ephemeral timeline items must be notices"),
|
|
1621
|
+
})
|
|
1622
|
+
.strip();
|
|
1623
|
+
const sessionProtocolEphemeralEventSchema = z.union([
|
|
1624
|
+
sessionProtocolEphemeralAgentThreadUpdateEventSchema,
|
|
1625
|
+
sessionProtocolFeedbackEventSchema,
|
|
1626
|
+
sessionProtocolEphemeralTimelineEventSchema,
|
|
1627
|
+
]);
|
|
1353
1628
|
const sessionProtocolEphemeralMessageSchema = z
|
|
1354
1629
|
.object({
|
|
1355
1630
|
version: z.literal(SESSION_PROTOCOL_VERSION),
|
|
@@ -1594,7 +1869,7 @@ export function createSessionProtocolDeltaMessage(options) {
|
|
|
1594
1869
|
sessionId: options.sessionId,
|
|
1595
1870
|
fromRevision: options.fromRevision,
|
|
1596
1871
|
toRevision: options.toRevision,
|
|
1597
|
-
|
|
1872
|
+
cause: options.cause,
|
|
1598
1873
|
delta: options.delta,
|
|
1599
1874
|
};
|
|
1600
1875
|
const parsedMessage = sessionProtocolDeltaMessageSchema.safeParse(message);
|
|
@@ -1629,11 +1904,33 @@ export function createSessionProtocolPendingUserMessagesMessage(options) {
|
|
|
1629
1904
|
}
|
|
1630
1905
|
return parsedMessage.data;
|
|
1631
1906
|
}
|
|
1907
|
+
function validateSnapshotResetCause(snapshot, message) {
|
|
1908
|
+
if (message.delta.type !== "snapshot.reset") {
|
|
1909
|
+
return;
|
|
1910
|
+
}
|
|
1911
|
+
if (message.cause.type === "compaction" &&
|
|
1912
|
+
message.cause.previousEpoch !== snapshot.timeline.epoch) {
|
|
1913
|
+
throw new Error(`compaction delta previous epoch ${message.cause.previousEpoch} does not match current timeline epoch ${snapshot.timeline.epoch}`);
|
|
1914
|
+
}
|
|
1915
|
+
if (message.cause.type !== "rewind") {
|
|
1916
|
+
return;
|
|
1917
|
+
}
|
|
1918
|
+
if (message.cause.epoch !== snapshot.timeline.epoch) {
|
|
1919
|
+
throw new Error(`rewind delta epoch ${message.cause.epoch} does not match current timeline epoch ${snapshot.timeline.epoch}`);
|
|
1920
|
+
}
|
|
1921
|
+
if (message.cause.cutoffSequence > snapshot.timeline.sequence) {
|
|
1922
|
+
throw new Error(`rewind delta cutoff sequence ${message.cause.cutoffSequence} exceeds current timeline sequence ${snapshot.timeline.sequence}`);
|
|
1923
|
+
}
|
|
1924
|
+
if (message.delta.snapshot.timeline.sequence !== snapshot.timeline.sequence) {
|
|
1925
|
+
throw new Error(`rewind delta timeline sequence ${message.delta.snapshot.timeline.sequence} does not preserve current timeline sequence ${snapshot.timeline.sequence}`);
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1632
1928
|
export function applySessionProtocolDelta(snapshot, message) {
|
|
1633
1929
|
if (message.sessionId !== snapshot.sessionId) {
|
|
1634
1930
|
throw new Error(`session delta targets '${message.sessionId}' but current snapshot is '${snapshot.sessionId}'`);
|
|
1635
1931
|
}
|
|
1636
1932
|
if (message.delta.type === "snapshot.reset") {
|
|
1933
|
+
validateSnapshotResetCause(snapshot, message);
|
|
1637
1934
|
return structuredClone(message.delta.snapshot);
|
|
1638
1935
|
}
|
|
1639
1936
|
if (message.fromRevision !== snapshot.revision) {
|
|
@@ -1667,11 +1964,11 @@ export function applySessionProtocolDelta(snapshot, message) {
|
|
|
1667
1964
|
case "settings.set":
|
|
1668
1965
|
next.settings = structuredClone(change.settings);
|
|
1669
1966
|
break;
|
|
1967
|
+
case "turn.set":
|
|
1968
|
+
setSessionProtocolTurn(next.turns, change.turn);
|
|
1969
|
+
break;
|
|
1670
1970
|
case "message.append":
|
|
1671
1971
|
next.messages.push(structuredClone(change.message));
|
|
1672
|
-
if (change.timelineItem) {
|
|
1673
|
-
next.timeline.push(structuredClone(change.timelineItem));
|
|
1674
|
-
}
|
|
1675
1972
|
break;
|
|
1676
1973
|
case "message.replace": {
|
|
1677
1974
|
const index = next.messages.findIndex((entry) => entry.id === change.message.id);
|
|
@@ -1688,20 +1985,23 @@ export function applySessionProtocolDelta(snapshot, message) {
|
|
|
1688
1985
|
break;
|
|
1689
1986
|
}
|
|
1690
1987
|
case "timeline.append":
|
|
1691
|
-
next.timeline.
|
|
1692
|
-
|
|
1693
|
-
case "timeline.replace": {
|
|
1694
|
-
const index = next.timeline.findIndex((item) => item.id === change.item.id);
|
|
1695
|
-
if (index === -1) {
|
|
1696
|
-
next.timeline.push(structuredClone(change.item));
|
|
1697
|
-
}
|
|
1698
|
-
else {
|
|
1699
|
-
next.timeline[index] = structuredClone(change.item);
|
|
1988
|
+
if (change.item.sequence <= next.timeline.sequence) {
|
|
1989
|
+
throw new Error(`timeline append sequence ${change.item.sequence} must exceed current timeline sequence ${next.timeline.sequence}`);
|
|
1700
1990
|
}
|
|
1991
|
+
next.timeline.items.push(structuredClone(change.item));
|
|
1992
|
+
next.timeline.sequence = change.item.sequence;
|
|
1701
1993
|
break;
|
|
1702
|
-
}
|
|
1703
1994
|
case "timeline.remove":
|
|
1704
|
-
next.timeline = next.timeline.filter((item) => item.id !== change.id);
|
|
1995
|
+
next.timeline.items = next.timeline.items.filter((item) => item.id !== change.id);
|
|
1996
|
+
break;
|
|
1997
|
+
case "timeline.advance":
|
|
1998
|
+
if (next.timeline.epoch !== change.epoch) {
|
|
1999
|
+
throw new Error("timeline advance epoch does not match the active epoch");
|
|
2000
|
+
}
|
|
2001
|
+
if (change.sequence <= next.timeline.sequence) {
|
|
2002
|
+
throw new Error(`timeline advance sequence ${change.sequence} must exceed current timeline sequence ${next.timeline.sequence}`);
|
|
2003
|
+
}
|
|
2004
|
+
next.timeline.sequence = change.sequence;
|
|
1705
2005
|
break;
|
|
1706
2006
|
case "tool.set":
|
|
1707
2007
|
next.tools[change.tool.id] = structuredClone(change.tool);
|
|
@@ -1709,6 +2009,12 @@ export function applySessionProtocolDelta(snapshot, message) {
|
|
|
1709
2009
|
case "tool.remove":
|
|
1710
2010
|
delete next.tools[change.id];
|
|
1711
2011
|
break;
|
|
2012
|
+
case "operation.set":
|
|
2013
|
+
next.operations[change.operation.id] = structuredClone(change.operation);
|
|
2014
|
+
break;
|
|
2015
|
+
case "operation.remove":
|
|
2016
|
+
delete next.operations[change.id];
|
|
2017
|
+
break;
|
|
1712
2018
|
case "agent.set":
|
|
1713
2019
|
next.agents[change.agent.id] = structuredClone(change.agent);
|
|
1714
2020
|
break;
|
|
@@ -1741,6 +2047,7 @@ function changeCanInvalidateSnapshot(snapshot, change) {
|
|
|
1741
2047
|
case "goal.set":
|
|
1742
2048
|
case "cost.set":
|
|
1743
2049
|
case "settings.set":
|
|
2050
|
+
case "turn.set":
|
|
1744
2051
|
case "agent.set":
|
|
1745
2052
|
return false;
|
|
1746
2053
|
case "tool.set": {
|
|
@@ -1751,17 +2058,16 @@ function changeCanInvalidateSnapshot(snapshot, change) {
|
|
|
1751
2058
|
const previous = snapshot.facets[change.facet.id];
|
|
1752
2059
|
return !previous || !hasSameFacetSubject(previous, change.facet);
|
|
1753
2060
|
}
|
|
1754
|
-
case "timeline.replace": {
|
|
1755
|
-
const previous = snapshot.timeline.find((item) => item.id === change.item.id);
|
|
1756
|
-
return !previous || !hasSameTimelineReferences(previous, change.item);
|
|
1757
|
-
}
|
|
1758
2061
|
case "agent-state.set":
|
|
1759
2062
|
case "message.append":
|
|
1760
2063
|
case "message.replace":
|
|
1761
2064
|
case "message.content.append":
|
|
1762
2065
|
case "timeline.append":
|
|
1763
2066
|
case "timeline.remove":
|
|
2067
|
+
case "timeline.advance":
|
|
1764
2068
|
case "tool.remove":
|
|
2069
|
+
case "operation.set":
|
|
2070
|
+
case "operation.remove":
|
|
1765
2071
|
case "agent.remove":
|
|
1766
2072
|
case "facet.remove":
|
|
1767
2073
|
return true;
|
|
@@ -1791,25 +2097,41 @@ function hasSameFacetSubject(previous, facet) {
|
|
|
1791
2097
|
(previous.subject.type === "session" ||
|
|
1792
2098
|
(facet.subject.type !== "session" && previous.subject.id === facet.subject.id)));
|
|
1793
2099
|
}
|
|
1794
|
-
function
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
2100
|
+
function setSessionProtocolTurn(turns, turn) {
|
|
2101
|
+
const previous = Object.hasOwn(turns, turn.userHistoryEntryId)
|
|
2102
|
+
? turns[turn.userHistoryEntryId]
|
|
2103
|
+
: undefined;
|
|
2104
|
+
if (previous?.state === "settled" && JSON.stringify(previous) !== JSON.stringify(turn)) {
|
|
2105
|
+
throw new Error(`settled turn '${turn.userHistoryEntryId}' cannot be changed`);
|
|
1799
2106
|
}
|
|
1800
|
-
|
|
2107
|
+
Object.defineProperty(turns, turn.userHistoryEntryId, {
|
|
2108
|
+
configurable: true,
|
|
2109
|
+
enumerable: true,
|
|
2110
|
+
value: structuredClone(turn),
|
|
2111
|
+
writable: true,
|
|
2112
|
+
});
|
|
1801
2113
|
}
|
|
1802
2114
|
function applyKeyedRecordDelta(snapshot, message) {
|
|
1803
2115
|
if (message.delta.type !== "snapshot.patch") {
|
|
1804
2116
|
return undefined;
|
|
1805
2117
|
}
|
|
2118
|
+
let nextTurns;
|
|
1806
2119
|
let nextTools;
|
|
2120
|
+
let nextOperations;
|
|
1807
2121
|
let nextAgents;
|
|
1808
2122
|
let nextFacets;
|
|
2123
|
+
const cloneTurns = () => {
|
|
2124
|
+
nextTurns ??= { ...snapshot.turns };
|
|
2125
|
+
return nextTurns;
|
|
2126
|
+
};
|
|
1809
2127
|
const cloneTools = () => {
|
|
1810
2128
|
nextTools ??= { ...snapshot.tools };
|
|
1811
2129
|
return nextTools;
|
|
1812
2130
|
};
|
|
2131
|
+
const cloneOperations = () => {
|
|
2132
|
+
nextOperations ??= { ...snapshot.operations };
|
|
2133
|
+
return nextOperations;
|
|
2134
|
+
};
|
|
1813
2135
|
const cloneAgents = () => {
|
|
1814
2136
|
nextAgents ??= { ...snapshot.agents };
|
|
1815
2137
|
return nextAgents;
|
|
@@ -1820,12 +2142,21 @@ function applyKeyedRecordDelta(snapshot, message) {
|
|
|
1820
2142
|
};
|
|
1821
2143
|
for (const change of message.delta.changes) {
|
|
1822
2144
|
switch (change.type) {
|
|
2145
|
+
case "turn.set":
|
|
2146
|
+
setSessionProtocolTurn(cloneTurns(), change.turn);
|
|
2147
|
+
break;
|
|
1823
2148
|
case "tool.set":
|
|
1824
2149
|
cloneTools()[change.tool.id] = structuredClone(change.tool);
|
|
1825
2150
|
break;
|
|
1826
2151
|
case "tool.remove":
|
|
1827
2152
|
delete cloneTools()[change.id];
|
|
1828
2153
|
break;
|
|
2154
|
+
case "operation.set":
|
|
2155
|
+
cloneOperations()[change.operation.id] = structuredClone(change.operation);
|
|
2156
|
+
break;
|
|
2157
|
+
case "operation.remove":
|
|
2158
|
+
delete cloneOperations()[change.id];
|
|
2159
|
+
break;
|
|
1829
2160
|
case "agent.set":
|
|
1830
2161
|
cloneAgents()[change.agent.id] = structuredClone(change.agent);
|
|
1831
2162
|
break;
|
|
@@ -1845,7 +2176,9 @@ function applyKeyedRecordDelta(snapshot, message) {
|
|
|
1845
2176
|
return {
|
|
1846
2177
|
...snapshot,
|
|
1847
2178
|
revision: message.toRevision,
|
|
2179
|
+
...(nextTurns ? { turns: nextTurns } : {}),
|
|
1848
2180
|
...(nextTools ? { tools: nextTools } : {}),
|
|
2181
|
+
...(nextOperations ? { operations: nextOperations } : {}),
|
|
1849
2182
|
...(nextAgents ? { agents: nextAgents } : {}),
|
|
1850
2183
|
...(nextFacets ? { facets: nextFacets } : {}),
|
|
1851
2184
|
};
|
|
@@ -2730,8 +3063,8 @@ function parseSessionProtocolDeltaMessage(payload) {
|
|
|
2730
3063
|
if (hasIssue(deltaMessage.error, ["toRevision"])) {
|
|
2731
3064
|
return fail("session.delta.toRevision must be a positive integer");
|
|
2732
3065
|
}
|
|
2733
|
-
if (hasIssue(deltaMessage.error, ["
|
|
2734
|
-
return fail("session.delta.
|
|
3066
|
+
if (hasIssue(deltaMessage.error, ["cause"])) {
|
|
3067
|
+
return fail("session.delta.cause is invalid");
|
|
2735
3068
|
}
|
|
2736
3069
|
if (hasIssue(deltaMessage.error, ["delta"])) {
|
|
2737
3070
|
return fail("session.delta.delta is invalid");
|
|
@@ -2746,7 +3079,7 @@ function parseSessionProtocolDeltaMessage(payload) {
|
|
|
2746
3079
|
sessionId: deltaMessage.data.sessionId,
|
|
2747
3080
|
fromRevision: deltaMessage.data.fromRevision,
|
|
2748
3081
|
toRevision: deltaMessage.data.toRevision,
|
|
2749
|
-
|
|
3082
|
+
cause: deltaMessage.data.cause,
|
|
2750
3083
|
delta: deltaMessage.data.delta,
|
|
2751
3084
|
},
|
|
2752
3085
|
};
|