@zhin.js/host-http 1.1.10 → 1.1.11

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.
@@ -66,6 +66,7 @@ export interface ConsoleAgentSessionTree {
66
66
  resolveActiveSessionId(sessionKey: string): Promise<string | null>;
67
67
  getActiveLeafMessageId(sessionId: string): Promise<number | null>;
68
68
  listBranchPoints(sessionId: string): Promise<readonly unknown[]>;
69
+ readBranchMessage?(sessionId: string, messageId: number): Promise<string | null>;
69
70
  switchActiveLeaf(sessionId: string, messageId: number): Promise<boolean>;
70
71
  jumpToBranchIndex(sessionId: string, index: number): Promise<{
71
72
  ok: boolean;
@@ -676,13 +676,22 @@ function registerAgentSessionRoutes(route, base, ctx) {
676
676
  error: 'Agent session tree runtime 未就绪(新 Runtime 需经 acquireAgentRuntime 装配 sessionTree)',
677
677
  });
678
678
  };
679
- // HttpHost 无 `:param` 路由,用前缀路由匹配 `<sessionKey>/tree|leaf`。
679
+ // HttpHost 无 `:param` 路由,用前缀路由匹配 `<sessionKey>/tree|message|leaf`。
680
680
  route('GET', `${base}/agent/sessions/*`, async (_request, response, url) => {
681
- const parsed = parseSessionAction(url.pathname, prefix, 'tree');
681
+ const messageAction = parseSessionAction(url.pathname, prefix, 'message');
682
+ if (messageAction)
683
+ response.setHeader('Cache-Control', 'no-store');
684
+ const parsed = messageAction ?? parseSessionAction(url.pathname, prefix, 'tree');
682
685
  if (!parsed) {
683
686
  writeJson(response, 404, { success: false, error: 'Not found' });
684
687
  return;
685
688
  }
689
+ const messageId = Number(url.searchParams.get('messageId'));
690
+ const expectedSessionId = url.searchParams.get('sessionId');
691
+ if (messageAction && (!Number.isSafeInteger(messageId) || messageId < 1 || !expectedSessionId)) {
692
+ writeJson(response, 400, { success: false, error: '需要 sessionId 和正安全整数 messageId' });
693
+ return;
694
+ }
686
695
  const lease = acquireSessionTree();
687
696
  if (!lease) {
688
697
  unavailable(response);
@@ -690,7 +699,28 @@ function registerAgentSessionRoutes(route, base, ctx) {
690
699
  }
691
700
  const runtime = lease.tree;
692
701
  try {
702
+ if (messageAction && !runtime.readBranchMessage) {
703
+ writeJson(response, 501, { success: false, error: '当前 Agent runtime 不支持读取完整消息' });
704
+ return;
705
+ }
693
706
  const sessionId = await runtime.resolveActiveSessionId(parsed.sessionKey);
707
+ if (messageAction) {
708
+ if (!sessionId) {
709
+ writeJson(response, 404, { success: false, error: '未找到活跃会话' });
710
+ return;
711
+ }
712
+ if (sessionId !== expectedSessionId) {
713
+ writeJson(response, 409, { success: false, error: '会话已变化,请重新读取会话列表' });
714
+ return;
715
+ }
716
+ const text = await runtime.readBranchMessage(sessionId, messageId);
717
+ if (text === null) {
718
+ writeJson(response, 404, { success: false, error: '未找到此会话的用户消息' });
719
+ return;
720
+ }
721
+ writeJson(response, 200, { success: true, data: { messageId, text } });
722
+ return;
723
+ }
694
724
  if (!sessionId) {
695
725
  if (!isRuntimeImSessionKey(parsed.sessionKey)) {
696
726
  writeJson(response, 404, {
@@ -738,7 +768,7 @@ function registerAgentSessionRoutes(route, base, ctx) {
738
768
  finally {
739
769
  lease.release();
740
770
  }
741
- }, { summary: 'Get agent session tree', tags: ['console', 'agent'] });
771
+ }, { summary: 'Get agent session tree or message', tags: ['console', 'agent'] });
742
772
  route('POST', `${base}/agent/sessions/*`, async (request, response, url, authScope) => {
743
773
  const parsed = parseSessionAction(url.pathname, prefix, 'leaf');
744
774
  if (!parsed) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/host-http",
3
- "version": "1.1.10",
3
+ "version": "1.1.11",
4
4
  "description": "Minimal HTTP and WebSocket host for Plugin Runtime (no Koa)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -71,6 +71,7 @@ export interface ConsoleAgentSessionTree {
71
71
  resolveActiveSessionId(sessionKey: string): Promise<string | null>;
72
72
  getActiveLeafMessageId(sessionId: string): Promise<number | null>;
73
73
  listBranchPoints(sessionId: string): Promise<readonly unknown[]>;
74
+ readBranchMessage?(sessionId: string, messageId: number): Promise<string | null>;
74
75
  switchActiveLeaf(sessionId: string, messageId: number): Promise<boolean>;
75
76
  jumpToBranchIndex(
76
77
  sessionId: string,
@@ -872,13 +873,21 @@ function registerAgentSessionRoutes(
872
873
  });
873
874
  };
874
875
 
875
- // HttpHost 无 `:param` 路由,用前缀路由匹配 `<sessionKey>/tree|leaf`。
876
+ // HttpHost 无 `:param` 路由,用前缀路由匹配 `<sessionKey>/tree|message|leaf`。
876
877
  route('GET', `${base}/agent/sessions/*`, async (_request, response, url) => {
877
- const parsed = parseSessionAction(url.pathname, prefix, 'tree');
878
+ const messageAction = parseSessionAction(url.pathname, prefix, 'message');
879
+ if (messageAction) response.setHeader('Cache-Control', 'no-store');
880
+ const parsed = messageAction ?? parseSessionAction(url.pathname, prefix, 'tree');
878
881
  if (!parsed) {
879
882
  writeJson(response, 404, { success: false, error: 'Not found' });
880
883
  return;
881
884
  }
885
+ const messageId = Number(url.searchParams.get('messageId'));
886
+ const expectedSessionId = url.searchParams.get('sessionId');
887
+ if (messageAction && (!Number.isSafeInteger(messageId) || messageId < 1 || !expectedSessionId)) {
888
+ writeJson(response, 400, { success: false, error: '需要 sessionId 和正安全整数 messageId' });
889
+ return;
890
+ }
882
891
  const lease = acquireSessionTree();
883
892
  if (!lease) {
884
893
  unavailable(response);
@@ -886,7 +895,28 @@ function registerAgentSessionRoutes(
886
895
  }
887
896
  const runtime = lease.tree;
888
897
  try {
898
+ if (messageAction && !runtime.readBranchMessage) {
899
+ writeJson(response, 501, { success: false, error: '当前 Agent runtime 不支持读取完整消息' });
900
+ return;
901
+ }
889
902
  const sessionId = await runtime.resolveActiveSessionId(parsed.sessionKey);
903
+ if (messageAction) {
904
+ if (!sessionId) {
905
+ writeJson(response, 404, { success: false, error: '未找到活跃会话' });
906
+ return;
907
+ }
908
+ if (sessionId !== expectedSessionId) {
909
+ writeJson(response, 409, { success: false, error: '会话已变化,请重新读取会话列表' });
910
+ return;
911
+ }
912
+ const text = await runtime.readBranchMessage!(sessionId, messageId);
913
+ if (text === null) {
914
+ writeJson(response, 404, { success: false, error: '未找到此会话的用户消息' });
915
+ return;
916
+ }
917
+ writeJson(response, 200, { success: true, data: { messageId, text } });
918
+ return;
919
+ }
890
920
  if (!sessionId) {
891
921
  if (!isRuntimeImSessionKey(parsed.sessionKey)) {
892
922
  writeJson(response, 404, {
@@ -934,7 +964,7 @@ function registerAgentSessionRoutes(
934
964
  } finally {
935
965
  lease.release();
936
966
  }
937
- }, { summary: 'Get agent session tree', tags: ['console', 'agent'] });
967
+ }, { summary: 'Get agent session tree or message', tags: ['console', 'agent'] });
938
968
 
939
969
  route('POST', `${base}/agent/sessions/*`, async (request, response, url, authScope) => {
940
970
  const parsed = parseSessionAction(url.pathname, prefix, 'leaf');
@@ -1038,7 +1068,7 @@ function isRuntimeImSessionKey(value: string): boolean {
1038
1068
  function parseSessionAction(
1039
1069
  pathname: string,
1040
1070
  prefix: string,
1041
- action: 'tree' | 'leaf',
1071
+ action: 'tree' | 'message' | 'leaf',
1042
1072
  ): { sessionKey: string } | null {
1043
1073
  if (!pathname.startsWith(prefix)) return null;
1044
1074
  const rest = pathname.slice(prefix.length).replace(/\/+$/u, '');