@adecore/agent-contracts 0.49.6 → 0.50.0

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/chat.js CHANGED
@@ -644,14 +644,24 @@ export const ChatItemSchema = z.discriminatedUnion('kind', [
644
644
  export function activeSubagents(items) {
645
645
  const lastMessage = items.findLastIndex((item) => item.kind === 'user');
646
646
  const lastMessageAt = lastMessage < 0 ? null : items[lastMessage].createdAt;
647
- const rows = items.flatMap((item, index) => (item.kind === 'subagent' ? [{ item, index }] : []));
648
- const turns = new Set(rows.flatMap(({ item }) => (item.status === 'running' && item.turnId !== null ? [item.turnId] : [])));
649
- return rows
650
- .filter(({ item, index }) => item.status === 'running' ||
651
- index > lastMessage ||
652
- (lastMessageAt !== null && item.finishedAt !== null && item.finishedAt > lastMessageAt) ||
653
- (item.turnId !== null && turns.has(item.turnId)))
654
- .map(({ item }) => item);
647
+ const turns = new Set();
648
+ for (const item of items) {
649
+ if (item.kind === 'subagent' && item.status === 'running' && item.turnId !== null) {
650
+ turns.add(item.turnId);
651
+ }
652
+ }
653
+ const active = [];
654
+ for (let index = 0; index < items.length; index++) {
655
+ const item = items[index];
656
+ if (item.kind === 'subagent' &&
657
+ (item.status === 'running' ||
658
+ index > lastMessage ||
659
+ (lastMessageAt !== null && item.finishedAt !== null && item.finishedAt > lastMessageAt) ||
660
+ (item.turnId !== null && turns.has(item.turnId)))) {
661
+ active.push(item);
662
+ }
663
+ }
664
+ return active;
655
665
  }
656
666
  /*
657
667
  * When a workflow last did something it could be shown for: the end the host noted, else the last end
@@ -691,21 +701,24 @@ export const CHAT_OUTLINE_TEXT_CHARS = 280;
691
701
  * A message of only attachments keeps their names. `items` is in thread order.
692
702
  */
693
703
  export function outlineItems(items, marked = new Set()) {
694
- return items.flatMap((item) => {
704
+ const outline = [];
705
+ for (const item of items) {
695
706
  const { id, createdAt, turnId } = item;
696
707
  if (item.kind === 'user') {
697
708
  const text = item.text.slice(0, CHAT_OUTLINE_TEXT_CHARS);
698
- return [{ id, createdAt, turnId, kind: 'user', text, ...(text === '' && item.attachments ? { attachments: item.attachments } : {}) }];
709
+ outline.push({ id, createdAt, turnId, kind: 'user', text, ...(text === '' && item.attachments ? { attachments: item.attachments } : {}) });
710
+ continue;
699
711
  }
700
712
  if (item.kind === 'turn' && ((item.taskIds?.length ?? 0) > 0 || (item.messageFrom?.length ?? 0) > 0)) {
701
713
  const { checkpointDiff: _checkpointDiff, ...turn } = item;
702
- return [turn];
714
+ outline.push(turn);
715
+ continue;
703
716
  }
704
717
  if (item.kind === 'assistant' && marked.has(id)) {
705
- return [{ id, createdAt, turnId, kind: 'assistant', text: item.text.slice(0, CHAT_OUTLINE_TEXT_CHARS), streaming: false }];
718
+ outline.push({ id, createdAt, turnId, kind: 'assistant', text: item.text.slice(0, CHAT_OUTLINE_TEXT_CHARS), streaming: false });
706
719
  }
707
- return [];
708
- });
720
+ }
721
+ return outline;
709
722
  }
710
723
  // Events apply after the `chat.attach` snapshot; item ids make upserts and streamed deltas replayable.
711
724
  export const ChatEventSchema = z.discriminatedUnion('type', [
package/dist/envelope.js CHANGED
@@ -36,5 +36,14 @@ export function parseRequest(input) {
36
36
  return toResult(RequestSchema.safeParse(input));
37
37
  }
38
38
  export function parseServerFrame(input) {
39
+ if (typeof input === 'object' && input !== null) {
40
+ const frame = input;
41
+ if (!('ok' in input) && frame.type === 'event' && typeof frame.event === 'string' && frame.event !== '') {
42
+ const event = EventSchema.safeParse(input);
43
+ if (event.success) {
44
+ return toResult(event);
45
+ }
46
+ }
47
+ }
39
48
  return toResult(ServerFrameSchema.safeParse(input));
40
49
  }
package/dist/text.js CHANGED
@@ -8,6 +8,10 @@ export function clipText(text, max) {
8
8
  if (text.length <= max) {
9
9
  return text;
10
10
  }
11
+ if (Number.isInteger(max) && max >= 0 && /^\p{ASCII}*$/u.test(text.slice(0, max + 1))) {
12
+ // CRLF is the only multi-unit ASCII grapheme; inspect the next unit before cutting.
13
+ return text.slice(0, text[max - 1] === '\r' && text[max] === '\n' ? max - 1 : max);
14
+ }
11
15
  graphemes ??= new Intl.Segmenter(undefined, { granularity: 'grapheme' });
12
16
  let end = 0;
13
17
  for (const { segment } of graphemes.segment(text)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/agent-contracts",
3
- "version": "0.49.6",
3
+ "version": "0.50.0",
4
4
  "description": "Schemas and port contracts for agent chats, provider accounts, models and usage.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -86,7 +86,7 @@
86
86
  "test": "bun --conditions=source test"
87
87
  },
88
88
  "dependencies": {
89
- "@adecore/intelligent-ui": "0.49.6",
89
+ "@adecore/intelligent-ui": "0.50.0",
90
90
  "zod": "^4.6.7"
91
91
  },
92
92
  "devDependencies": {
package/src/chat.ts CHANGED
@@ -779,18 +779,31 @@ export type ChatCompactionItem = z.infer<typeof ChatCompactionItemSchema>;
779
779
  export function activeSubagents(items: readonly ChatItem[]): ChatSubagentItem[] {
780
780
  const lastMessage = items.findLastIndex((item) => item.kind === 'user');
781
781
  const lastMessageAt = lastMessage < 0 ? null : items[lastMessage]!.createdAt;
782
- const rows = items.flatMap((item, index) => (item.kind === 'subagent' ? [{ item, index }] : []));
783
- const turns = new Set(rows.flatMap(({ item }) => (item.status === 'running' && item.turnId !== null ? [item.turnId] : [])));
782
+ const turns = new Set<string>();
784
783
 
785
- return rows
786
- .filter(
787
- ({ item, index }) =>
788
- item.status === 'running' ||
784
+ for (const item of items) {
785
+ if (item.kind === 'subagent' && item.status === 'running' && item.turnId !== null) {
786
+ turns.add(item.turnId);
787
+ }
788
+ }
789
+
790
+ const active: ChatSubagentItem[] = [];
791
+
792
+ for (let index = 0; index < items.length; index++) {
793
+ const item = items[index]!;
794
+
795
+ if (
796
+ item.kind === 'subagent' &&
797
+ (item.status === 'running' ||
789
798
  index > lastMessage ||
790
799
  (lastMessageAt !== null && item.finishedAt !== null && item.finishedAt > lastMessageAt) ||
791
- (item.turnId !== null && turns.has(item.turnId))
792
- )
793
- .map(({ item }) => item);
800
+ (item.turnId !== null && turns.has(item.turnId)))
801
+ ) {
802
+ active.push(item);
803
+ }
804
+ }
805
+
806
+ return active;
794
807
  }
795
808
 
796
809
  /*
@@ -840,27 +853,33 @@ export const CHAT_OUTLINE_TEXT_CHARS = 280;
840
853
  * A message of only attachments keeps their names. `items` is in thread order.
841
854
  */
842
855
  export function outlineItems(items: readonly ChatItem[], marked: ReadonlySet<string> = new Set()): ChatItem[] {
843
- return items.flatMap((item): ChatItem[] => {
856
+ const outline: ChatItem[] = [];
857
+
858
+ for (const item of items) {
844
859
  const { id, createdAt, turnId } = item;
845
860
 
846
861
  if (item.kind === 'user') {
847
862
  const text = item.text.slice(0, CHAT_OUTLINE_TEXT_CHARS);
848
863
 
849
- return [{ id, createdAt, turnId, kind: 'user', text, ...(text === '' && item.attachments ? { attachments: item.attachments } : {}) }];
864
+ outline.push({ id, createdAt, turnId, kind: 'user', text, ...(text === '' && item.attachments ? { attachments: item.attachments } : {}) });
865
+
866
+ continue;
850
867
  }
851
868
 
852
869
  if (item.kind === 'turn' && ((item.taskIds?.length ?? 0) > 0 || (item.messageFrom?.length ?? 0) > 0)) {
853
870
  const { checkpointDiff: _checkpointDiff, ...turn } = item;
854
871
 
855
- return [turn];
872
+ outline.push(turn);
873
+
874
+ continue;
856
875
  }
857
876
 
858
877
  if (item.kind === 'assistant' && marked.has(id)) {
859
- return [{ id, createdAt, turnId, kind: 'assistant', text: item.text.slice(0, CHAT_OUTLINE_TEXT_CHARS), streaming: false }];
878
+ outline.push({ id, createdAt, turnId, kind: 'assistant', text: item.text.slice(0, CHAT_OUTLINE_TEXT_CHARS), streaming: false });
860
879
  }
880
+ }
861
881
 
862
- return [];
863
- });
882
+ return outline;
864
883
  }
865
884
 
866
885
  // Events apply after the `chat.attach` snapshot; item ids make upserts and streamed deltas replayable.
package/src/envelope.ts CHANGED
@@ -56,5 +56,17 @@ export function parseRequest(input: unknown): ParseResult<Request> {
56
56
  }
57
57
 
58
58
  export function parseServerFrame(input: unknown): ParseResult<ServerFrame> {
59
+ if (typeof input === 'object' && input !== null) {
60
+ const frame = input as { type?: unknown; event?: unknown };
61
+
62
+ if (!('ok' in input) && frame.type === 'event' && typeof frame.event === 'string' && frame.event !== '') {
63
+ const event = EventSchema.safeParse(input);
64
+
65
+ if (event.success) {
66
+ return toResult(event);
67
+ }
68
+ }
69
+ }
70
+
59
71
  return toResult(ServerFrameSchema.safeParse(input));
60
72
  }
package/src/text.ts CHANGED
@@ -10,6 +10,11 @@ export function clipText(text: string, max: number): string {
10
10
  return text;
11
11
  }
12
12
 
13
+ if (Number.isInteger(max) && max >= 0 && /^\p{ASCII}*$/u.test(text.slice(0, max + 1))) {
14
+ // CRLF is the only multi-unit ASCII grapheme; inspect the next unit before cutting.
15
+ return text.slice(0, text[max - 1] === '\r' && text[max] === '\n' ? max - 1 : max);
16
+ }
17
+
13
18
  graphemes ??= new Intl.Segmenter(undefined, { granularity: 'grapheme' });
14
19
  let end = 0;
15
20