@rzx/ohs 1.2.24 → 1.2.25

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.
@@ -338,6 +338,23 @@ var init_validation = __esm(() => {
338
338
  });
339
339
 
340
340
  // ../../packages/domain-contracts/src/inputs/metadata.ts
341
+ function assertPromptMetadataAllowed(input) {
342
+ for (const namespace of ["metadata", "runMetadata"]) {
343
+ const metadata = input[namespace];
344
+ if (!metadata || typeof metadata !== "object")
345
+ continue;
346
+ if (Object.hasOwn(metadata, "outputSchema")) {
347
+ const field = `${namespace}.outputSchema`;
348
+ throw new DomainValidationError(`${field} is a stored snapshot; use explicit outputSchema`, field);
349
+ }
350
+ for (const name of ["pluginUi", "uiAction", "pluginUiSubmission", "pluginAppAction", "pluginAppSubmission", "pluginAppSource"]) {
351
+ if (Object.hasOwn(metadata, name)) {
352
+ const field = `${namespace}.${name}`;
353
+ throw new DomainValidationError(`${field} is reserved for the host`, field);
354
+ }
355
+ }
356
+ }
357
+ }
341
358
  var init_metadata = __esm(() => {
342
359
  init_validation_error();
343
360
  });
@@ -650,11 +667,43 @@ function decodeClearedSessionWorktreeBinding(value, expectedSessionId) {
650
667
  }
651
668
  return session;
652
669
  }
670
+ function decodeJobTarget(value) {
671
+ const target = object(value, "jobTarget");
672
+ const kind = enumField(target, "kind", "jobTarget", ["terminal", "shell", "agent", "dream", "workflow"]);
673
+ if (!stringField(target, "jobId", "jobTarget").trim())
674
+ throw new ProtocolDataError("jobTarget.jobId is required", "jobTarget.jobId");
675
+ if (kind === "agent" && !stringField(target, "runId", "jobTarget").trim())
676
+ throw new ProtocolDataError("jobTarget.runId is required", "jobTarget.runId");
677
+ return target;
678
+ }
679
+ function decodeJobInputReceipt(value) {
680
+ const receipt = object(value, "jobInputReceipt");
681
+ for (const field of ["sessionId", "inputId", "runId"]) {
682
+ if (!stringField(receipt, field, "jobInputReceipt").trim())
683
+ throw new ProtocolDataError(`jobInputReceipt.${field} is required`, `jobInputReceipt.${field}`);
684
+ }
685
+ return receipt;
686
+ }
687
+ function decodeJobListResult(value) {
688
+ const result = object(value, "jobListResult");
689
+ if (!Array.isArray(result.jobs) || !Array.isArray(result.errors))
690
+ throw new ProtocolDataError("Jobs list requires jobs and errors arrays", "jobListResult");
691
+ result.jobs.forEach(decodeJobSnapshot);
692
+ for (const error of result.errors) {
693
+ const item = object(error, "jobListResult.errors");
694
+ enumField(item, "kind", "jobListResult.errors", ["terminal", "shell", "agent", "dream", "workflow"]);
695
+ stringField(item, "message", "jobListResult.errors");
696
+ }
697
+ return result;
698
+ }
653
699
  function decodeJobSnapshot(value) {
654
700
  const job = object(value, "job");
655
701
  for (const field of ["id", "label", "ownerSession", "cwd"])
656
702
  stringField(job, field, "job");
657
703
  enumField(job, "kind", "job", ["terminal", "shell", "agent", "dream", "workflow"]);
704
+ const target = decodeJobTarget(job.target);
705
+ if (target.kind !== job.kind || target.jobId !== job.id)
706
+ throw new ProtocolDataError("job target does not match snapshot", "job.target");
658
707
  enumField(job, "status", "job", ["running", "stopping", "completed", "killed", "failed"]);
659
708
  const capabilities = recordField(job, "capabilities", "job");
660
709
  for (const field of ["read", "wait", "send", "cancel"]) {
@@ -1100,6 +1149,18 @@ var init_plugin_apps2 = __esm(() => {
1100
1149
  });
1101
1150
 
1102
1151
  // ../../packages/protocol/src/requests/requests.ts
1152
+ function assertPromptMetadataAllowed2(input) {
1153
+ adaptDomainInput(() => assertPromptMetadataAllowed(input));
1154
+ }
1155
+ function adaptDomainInput(read) {
1156
+ try {
1157
+ return read();
1158
+ } catch (error) {
1159
+ if (error instanceof DomainValidationError)
1160
+ throw new ProtocolValidationError(error.message, error.field, error.reason);
1161
+ throw error;
1162
+ }
1163
+ }
1103
1164
  var ProtocolValidationError;
1104
1165
  var init_requests = __esm(() => {
1105
1166
  init_inputs();
@@ -1359,7 +1420,7 @@ function parseAttachmentTransferCapabilities(value) {
1359
1420
  uploadModes
1360
1421
  };
1361
1422
  }
1362
- var CURRENT_PROTOCOL_VERSION = 5, PROTOCOL_VERSION_HEADER = "x-vykor-protocol-version";
1423
+ var CURRENT_PROTOCOL_VERSION = 7, PROTOCOL_VERSION_HEADER = "x-vykor-protocol-version";
1363
1424
  var init_capabilities = __esm(() => {
1364
1425
  init_attachment();
1365
1426
  });
@@ -1629,9 +1690,13 @@ var init_plugin_ui_history = __esm(() => {
1629
1690
  init_domain_validation();
1630
1691
  });
1631
1692
 
1693
+ // ../../packages/agent-contracts/src/tools/result-text.ts
1694
+ var init_result_text = () => {};
1695
+
1632
1696
  // ../../packages/agent-contracts/src/tools/tool-data.ts
1633
1697
  var MCP_CALL_RESULT_MAX_BYTES;
1634
1698
  var init_tool_data = __esm(() => {
1699
+ init_result_text();
1635
1700
  MCP_CALL_RESULT_MAX_BYTES = 1024 * 1024;
1636
1701
  });
1637
1702
 
@@ -2690,6 +2755,15 @@ class SessionResource {
2690
2755
  constructor(transport) {
2691
2756
  this.transport = transport;
2692
2757
  }
2758
+ async checkOutputSchema(input, signal) {
2759
+ assertPromptMetadataAllowed2(input);
2760
+ if (input.outputSchema === undefined)
2761
+ return;
2762
+ const capabilities = await this.transport.ensureProtocol(signal);
2763
+ if ((capabilities.features.structuredDelivery ?? 0) < 1) {
2764
+ throw new Error("Server does not support structured delivery (outputSchema)");
2765
+ }
2766
+ }
2693
2767
  async search(options) {
2694
2768
  const { signal, ...query } = options;
2695
2769
  const response = await this.transport.request(this.transport.path("/sessions/search", query), { signal });
@@ -2769,6 +2843,7 @@ class SessionResource {
2769
2843
  return response.part;
2770
2844
  }
2771
2845
  async admitPrompt(sessionId, input, options = {}) {
2846
+ await this.checkOutputSchema(input, options.signal);
2772
2847
  return await this.transport.request(`/sessions/${encodeURIComponent(sessionId)}/prompts`, {
2773
2848
  method: "POST",
2774
2849
  body: { ...input, id: input.id ?? createPromptRequestId() },
@@ -2776,6 +2851,7 @@ class SessionResource {
2776
2851
  });
2777
2852
  }
2778
2853
  async editLatestPrompt(sessionId, input, options = {}) {
2854
+ await this.checkOutputSchema(input, options.signal);
2779
2855
  return await this.transport.request(`/sessions/${encodeURIComponent(sessionId)}/prompts/latest/edit`, {
2780
2856
  method: "POST",
2781
2857
  body: input,
@@ -2789,6 +2865,7 @@ class SessionResource {
2789
2865
  return await this.transport.request(`/sessions/${encodeURIComponent(sessionId)}/prompts/${encodeURIComponent(inputId)}/cancel`, { method: "POST", body: input, signal: options.signal });
2790
2866
  }
2791
2867
  async resumeInterruptedRun(sessionId, runId, input = {}, options = {}) {
2868
+ await this.checkOutputSchema(input, options.signal);
2792
2869
  return await this.transport.request(`/sessions/${encodeURIComponent(sessionId)}/runs/${encodeURIComponent(runId)}/resume`, {
2793
2870
  method: "POST",
2794
2871
  body: { ...input, id: input.id ?? createPromptRequestId() },
@@ -3030,7 +3107,7 @@ class JobResource {
3030
3107
  ...statuses ? { statuses: statuses.join(",") } : {},
3031
3108
  ...includeFinished !== undefined ? { includeFinished: String(includeFinished) } : {}
3032
3109
  }), { signal });
3033
- return responseArray(response, "jobs", decodeJobSnapshot);
3110
+ return decodeJobListResult(response);
3034
3111
  }
3035
3112
  async createBackgroundShell(input, options = {}) {
3036
3113
  return await this.transport.request("/background-shells", {
@@ -3039,28 +3116,32 @@ class JobResource {
3039
3116
  signal: options.signal
3040
3117
  });
3041
3118
  }
3042
- async read(jobId, options) {
3119
+ async read(target, options) {
3043
3120
  const { signal, ...query } = options;
3044
- const response = await this.transport.request(this.transport.path(`/jobs/${encodeURIComponent(jobId)}`, query), { signal });
3121
+ const response = await this.transport.request(this.transport.path(`/jobs/${encodeURIComponent(target.jobId)}`, { ...query, kind: target.kind, ...target.kind === "agent" ? { runId: target.runId } : {} }), { signal });
3045
3122
  return decodeJobReadResult(response);
3046
3123
  }
3047
- async wait(jobId, input, options = {}) {
3048
- const response = await this.transport.request(`/jobs/${encodeURIComponent(jobId)}/wait`, {
3124
+ async wait(target, input, options = {}) {
3125
+ const response = await this.transport.request(`/jobs/${encodeURIComponent(target.jobId)}/wait`, {
3049
3126
  method: "POST",
3050
- body: input,
3127
+ body: { ...input, target },
3051
3128
  signal: options.signal
3052
3129
  });
3053
3130
  return decodeJobWaitResult(response);
3054
3131
  }
3055
- async send(jobId, input, options = {}) {
3056
- await this.transport.request(`/jobs/${encodeURIComponent(jobId)}/input`, {
3132
+ async send(target, input, options = {}) {
3133
+ const response = await this.transport.request(`/jobs/${encodeURIComponent(target.jobId)}/input`, {
3057
3134
  method: "POST",
3058
- body: input,
3135
+ body: { ...input, target },
3059
3136
  signal: options.signal
3060
3137
  });
3138
+ if (responseField(response, "sent") !== true)
3139
+ throw new ProtocolDataError("Job input was not accepted", "sent");
3140
+ const receipt = response.receipt;
3141
+ return receipt === undefined ? undefined : decodeJobInputReceipt(receipt);
3061
3142
  }
3062
- async cancel(jobId, input, options = {}) {
3063
- const response = await this.transport.request(`/jobs/${encodeURIComponent(jobId)}/cancel`, { method: "POST", body: input, signal: options.signal });
3143
+ async cancel(target, input, options = {}) {
3144
+ const response = await this.transport.request(`/jobs/${encodeURIComponent(target.jobId)}/cancel`, { method: "POST", body: { ...input, target }, signal: options.signal });
3064
3145
  return decodeJobSnapshot(responseField(response, "snapshot"));
3065
3146
  }
3066
3147
  }
@@ -3879,43 +3960,162 @@ var init_session_stream_state = __esm(() => {
3879
3960
  });
3880
3961
 
3881
3962
  // ../../packages/client/src/state/sync.ts
3882
- var init_sync = () => {};
3963
+ async function* runEventSync(client, options, initialState = createInitialClientState(), onLiveEvent) {
3964
+ let state = initialState;
3965
+ let cursor = Math.max(0, options.cursor ?? 0, state.lastSeq);
3966
+ let attempt = 0;
3967
+ const signal = options.signal;
3968
+ const delayMs = options.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;
3969
+ const reduce = options.sessionId ? applySessionStreamEvent : options.globalReducer ?? applyEvent;
3970
+ if (signal?.aborted)
3971
+ return;
3972
+ const refreshSession = async (initial) => {
3973
+ const snapshot = await client.sessions.getState(options.sessionId, {
3974
+ signal,
3975
+ partView: options.partView
3976
+ });
3977
+ if (signal?.aborted)
3978
+ return;
3979
+ const next = initial ? applySessionSnapshot(state, snapshot) : applySessionStreamSnapshot(state, snapshot);
3980
+ state = Object.keys(next.eventsBySeq).length ? { ...next, eventsBySeq: {} } : next;
3981
+ cursor = Math.max(cursor, snapshot.cursor, state.lastSeq);
3982
+ };
3983
+ try {
3984
+ if (options.sessionId) {
3985
+ await refreshSession(true);
3986
+ } else {
3987
+ const replay = await client.events.list({
3988
+ cursor: options.cursor,
3989
+ partView: options.partView,
3990
+ signal
3991
+ });
3992
+ if (signal?.aborted)
3993
+ return;
3994
+ for (const event of replay) {
3995
+ if (signal?.aborted)
3996
+ return;
3997
+ state = reduce(state, event);
3998
+ yield { event, state, source: "replay" };
3999
+ }
4000
+ cursor = Math.max(cursor, state.lastSeq);
4001
+ }
4002
+ if (signal?.aborted)
4003
+ return;
4004
+ yield { state, source: "snapshot" };
4005
+ let needsSnapshot = false;
4006
+ while (!signal?.aborted) {
4007
+ try {
4008
+ if (needsSnapshot) {
4009
+ await refreshSession(false);
4010
+ if (signal?.aborted)
4011
+ return;
4012
+ needsSnapshot = false;
4013
+ yield { state, source: "snapshot" };
4014
+ }
4015
+ if (signal?.aborted)
4016
+ return;
4017
+ for await (const event of client.events.stream({
4018
+ cursor,
4019
+ sessionId: options.sessionId,
4020
+ signal,
4021
+ partView: options.partView,
4022
+ transportReconnect: false,
4023
+ idleTimeoutMs: options.idleTimeoutMs
4024
+ })) {
4025
+ if (signal?.aborted)
4026
+ return;
4027
+ onLiveEvent?.();
4028
+ if (signal?.aborted)
4029
+ return;
4030
+ attempt = 0;
4031
+ if (event.seq > cursor + 1 && !options.sessionId) {
4032
+ const gap = await client.events.list({ cursor, partView: options.partView, signal });
4033
+ if (signal?.aborted)
4034
+ return;
4035
+ for (const missed of gap) {
4036
+ if (signal?.aborted)
4037
+ return;
4038
+ const before = state;
4039
+ state = reduce(state, missed);
4040
+ if (state !== before)
4041
+ yield { event: missed, state, source: "replay" };
4042
+ }
4043
+ cursor = Math.max(cursor, state.lastSeq);
4044
+ }
4045
+ if (signal?.aborted)
4046
+ return;
4047
+ const before = state;
4048
+ state = reduce(state, event);
4049
+ cursor = Math.max(cursor, state.lastSeq);
4050
+ if (state !== before)
4051
+ yield { event, state, source: "live" };
4052
+ }
4053
+ } catch (error) {
4054
+ if (error instanceof UnsupportedSessionEventSchemaVersionError)
4055
+ throw error;
4056
+ if (isAbortError(error) || signal?.aborted)
4057
+ return;
4058
+ }
4059
+ if (signal?.aborted)
4060
+ return;
4061
+ needsSnapshot = Boolean(options.sessionId);
4062
+ yield { state, source: "reconnecting" };
4063
+ if (!await waitForReconnect2(delayMs(attempt), signal))
4064
+ return;
4065
+ attempt += 1;
4066
+ }
4067
+ } catch (error) {
4068
+ if (!isAbortError(error) && !signal?.aborted)
4069
+ throw error;
4070
+ }
4071
+ }
4072
+ async function waitForReconnect2(ms, signal) {
4073
+ if (signal?.aborted)
4074
+ return false;
4075
+ if (ms <= 0)
4076
+ return true;
4077
+ return new Promise((resolve) => {
4078
+ const onAbort = () => {
4079
+ clearTimeout(timer);
4080
+ resolve(false);
4081
+ };
4082
+ const timer = setTimeout(() => {
4083
+ signal?.removeEventListener("abort", onAbort);
4084
+ resolve(!signal?.aborted);
4085
+ }, ms);
4086
+ signal?.addEventListener("abort", onAbort, { once: true });
4087
+ });
4088
+ }
4089
+ function isAbortError(error) {
4090
+ return Boolean(error && typeof error === "object" && "name" in error && error.name === "AbortError");
4091
+ }
4092
+ var DEFAULT_RECONNECT_DELAY_MS = (attempt) => Math.min(30000, 250 * 2 ** Math.max(0, attempt));
4093
+ var init_sync = __esm(() => {
4094
+ init_reducer();
4095
+ init_session_stream_state();
4096
+ });
3883
4097
 
3884
4098
  // ../../packages/client/src/state/session-sync-controller.ts
3885
4099
  class SessionSyncController {
3886
- client;
3887
- sessionId;
4100
+ options;
3888
4101
  initialCursor;
3889
4102
  generation;
3890
- externalSignal;
3891
- reconnectDelayMs;
3892
- onUpdate;
3893
- onStatusChange;
3894
- onError;
3895
4103
  abortController = new AbortController;
3896
4104
  state;
3897
4105
  status = "idle";
3898
4106
  running = false;
3899
4107
  constructor(options) {
3900
- this.client = options.client;
3901
- this.sessionId = options.sessionId;
4108
+ this.options = { ...options };
3902
4109
  this.state = options.initialState ?? createInitialClientState();
3903
4110
  if ((options.cursor ?? 0) > 0 && !options.initialState) {
3904
4111
  throw new Error("A non-zero cursor requires initialState so durable state is not skipped");
3905
4112
  }
3906
4113
  this.initialCursor = Math.max(0, options.cursor ?? 0, this.state.lastSeq);
3907
4114
  this.generation = options.generation ?? 0;
3908
- this.externalSignal = options.signal;
3909
- this.reconnectDelayMs = options.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;
3910
- this.onUpdate = options.onUpdate;
3911
- this.onStatusChange = options.onStatusChange;
3912
- this.onError = options.onError;
3913
- if (this.externalSignal) {
3914
- if (this.externalSignal.aborted) {
3915
- this.abortController.abort(this.externalSignal.reason);
3916
- } else {
3917
- this.externalSignal.addEventListener("abort", () => this.abortController.abort(this.externalSignal?.reason), { once: true });
3918
- }
4115
+ if (options.signal?.aborted) {
4116
+ this.abortController.abort(options.signal.reason);
4117
+ } else {
4118
+ options.signal?.addEventListener("abort", () => this.abort(options.signal?.reason), { once: true });
3919
4119
  }
3920
4120
  }
3921
4121
  get currentStatus() {
@@ -3937,169 +4137,59 @@ class SessionSyncController {
3937
4137
  if (this.status === status)
3938
4138
  return;
3939
4139
  this.status = status;
3940
- this.onStatusChange?.(status, this.generation);
3941
- }
3942
- emitUpdate(update) {
3943
- if (this.abortController.signal.aborted)
3944
- return;
3945
- this.state = update.state;
3946
- this.onUpdate?.(update, this.generation);
4140
+ this.options.onStatusChange?.(status, this.generation);
3947
4141
  }
3948
4142
  async start() {
3949
4143
  if (this.running)
3950
4144
  return;
3951
- this.running = true;
3952
- const signal = this.abortController.signal;
4145
+ const signal = this.signal;
3953
4146
  if (signal.aborted) {
3954
4147
  this.setStatus("idle");
3955
4148
  return;
3956
4149
  }
3957
- let cursor = this.initialCursor;
3958
- let attempt = 0;
3959
- const refreshSession = async (sessionId) => {
3960
- const snapshot = await this.client.sessions.getState(sessionId, { signal });
3961
- if (signal.aborted)
3962
- return;
3963
- const next = this.status === "connecting" ? applySessionSnapshot(this.state, snapshot) : applySessionStreamSnapshot(this.state, snapshot);
3964
- this.state = Object.keys(next.eventsBySeq).length ? { ...next, eventsBySeq: {} } : next;
3965
- cursor = Math.max(cursor, snapshot.cursor, this.state.lastSeq);
3966
- this.setStatus("connected");
3967
- this.emitUpdate({ state: this.state, source: "snapshot" });
3968
- };
4150
+ this.running = true;
3969
4151
  try {
3970
4152
  this.setStatus("connecting");
3971
- if (this.sessionId) {
3972
- await refreshSession(this.sessionId);
3973
- } else {
3974
- const replay = await this.client.events.list({
3975
- cursor,
3976
- sessionId: this.sessionId,
3977
- signal
3978
- });
4153
+ for await (const update of runEventSync(this.options.client, {
4154
+ sessionId: this.options.sessionId,
4155
+ cursor: this.initialCursor,
4156
+ signal,
4157
+ reconnectDelayMs: this.options.reconnectDelayMs
4158
+ }, this.state, () => this.setStatus("connected"))) {
3979
4159
  if (signal.aborted)
3980
4160
  return;
3981
- for (const event of replay) {
3982
- this.state = applyEvent(this.state, event);
3983
- this.emitUpdate({ event, state: this.state, source: "replay" });
3984
- }
3985
- cursor = Math.max(cursor, this.state.lastSeq);
3986
- this.setStatus("connected");
3987
- }
3988
- while (!signal.aborted) {
3989
- try {
3990
- if (this.sessionId && attempt > 0) {
3991
- await refreshSession(this.sessionId);
3992
- if (signal.aborted)
3993
- return;
3994
- }
3995
- for await (const event of this.client.events.stream({
3996
- cursor,
3997
- sessionId: this.sessionId,
3998
- signal,
3999
- transportReconnect: false
4000
- })) {
4001
- if (signal.aborted)
4002
- return;
4003
- attempt = 0;
4004
- this.setStatus("connected");
4005
- if (event.seq > cursor + 1 && !this.sessionId) {
4006
- const gap = await this.client.events.list({
4007
- cursor,
4008
- sessionId: this.sessionId,
4009
- signal
4010
- });
4011
- if (signal.aborted)
4012
- return;
4013
- for (const missed of gap) {
4014
- const beforeGap = this.state;
4015
- this.state = applyEvent(this.state, missed);
4016
- if (this.state !== beforeGap) {
4017
- this.emitUpdate({ event: missed, state: this.state, source: "replay" });
4018
- }
4019
- }
4020
- cursor = Math.max(cursor, this.state.lastSeq);
4021
- }
4022
- const before = this.state;
4023
- this.state = this.sessionId ? applySessionStreamEvent(this.state, event) : applyEvent(this.state, event);
4024
- cursor = Math.max(cursor, this.state.lastSeq);
4025
- if (this.state !== before) {
4026
- this.emitUpdate({ event, state: this.state, source: "live" });
4027
- }
4028
- }
4029
- if (signal.aborted)
4030
- return;
4031
- this.setStatus("reconnecting");
4032
- this.emitUpdate({ state: this.state, source: "reconnecting" });
4033
- const delay = this.reconnectDelayMs(attempt);
4034
- attempt += 1;
4035
- cursor = this.state.lastSeq;
4036
- if (!await this.waitForDelay(delay, signal))
4037
- return;
4038
- } catch (error) {
4039
- if (error instanceof UnsupportedSessionEventSchemaVersionError) {
4040
- throw error;
4041
- }
4042
- if (this.isAbortError(error) || signal.aborted)
4043
- return;
4161
+ this.state = update.state;
4162
+ if (update.source === "reconnecting")
4044
4163
  this.setStatus("reconnecting");
4045
- this.emitUpdate({ state: this.state, source: "reconnecting" });
4046
- const delay = this.reconnectDelayMs(attempt);
4047
- attempt += 1;
4048
- cursor = this.state.lastSeq;
4049
- if (!await this.waitForDelay(delay, signal))
4050
- return;
4051
- }
4164
+ else if (update.source !== "replay")
4165
+ this.setStatus("connected");
4166
+ if (signal.aborted)
4167
+ return;
4168
+ if (update.source === "snapshot" && !this.options.sessionId)
4169
+ continue;
4170
+ this.options.onUpdate?.(update, this.generation);
4052
4171
  }
4053
4172
  } catch (error) {
4054
- if (!this.isAbortError(error) && !signal.aborted) {
4173
+ if (!signal.aborted && !(error instanceof Error && error.name === "AbortError")) {
4055
4174
  this.setStatus("error");
4056
- this.onError?.(error, this.generation);
4175
+ this.options.onError?.(error, this.generation);
4057
4176
  }
4058
4177
  } finally {
4059
4178
  this.running = false;
4060
- if (signal.aborted && this.status !== "error") {
4179
+ if (signal.aborted && this.status !== "error")
4061
4180
  this.setStatus("idle");
4062
- }
4063
4181
  }
4064
4182
  }
4065
- isAbortError(error) {
4066
- if (!error || typeof error !== "object")
4067
- return false;
4068
- if ("name" in error && error.name === "AbortError")
4069
- return true;
4070
- return false;
4071
- }
4072
- waitForDelay(ms, signal) {
4073
- if (ms <= 0)
4074
- return Promise.resolve(!signal.aborted);
4075
- return new Promise((resolve) => {
4076
- if (signal.aborted) {
4077
- resolve(false);
4078
- return;
4079
- }
4080
- const timer = setTimeout(() => {
4081
- signal.removeEventListener("abort", onAbort);
4082
- resolve(!signal.aborted);
4083
- }, ms);
4084
- const onAbort = () => {
4085
- clearTimeout(timer);
4086
- resolve(false);
4087
- };
4088
- signal.addEventListener("abort", onAbort, { once: true });
4089
- });
4090
- }
4091
4183
  }
4092
- var DEFAULT_RECONNECT_DELAY_MS = (attempt) => Math.min(30000, 250 * 2 ** Math.max(0, attempt));
4093
4184
  var init_session_sync_controller = __esm(() => {
4094
4185
  init_reducer();
4095
- init_session_stream_state();
4186
+ init_sync();
4096
4187
  });
4097
4188
 
4098
4189
  // ../../packages/client/src/state/index.ts
4099
4190
  var init_state = __esm(() => {
4100
4191
  init_reducer();
4101
4192
  init_selectors();
4102
- init_sync();
4103
4193
  init_session_sync_controller();
4104
4194
  });
4105
4195
 
@@ -4158,12 +4248,12 @@ async function handleDiagnosticCommand(slash, host, emit, readPresentation) {
4158
4248
  const estimatedTokens = Math.max(1, Math.ceil(text.length / 4));
4159
4249
  const [memory, jobsResult, settings] = await Promise.all([
4160
4250
  client.system.listMemory({ cwd }).catch(() => ({ entries: [] })),
4161
- client.jobs.list({ sessionId, includeFinished: true, limit: 100 }).then((jobs) => ({ jobs })).catch((error) => ({
4251
+ client.jobs.list({ sessionId, includeFinished: true, limit: 100 }).then((listed) => ({ jobs: listed.jobs, errors: listed.errors })).catch((error) => ({
4162
4252
  error: error instanceof Error ? error.message : String(error)
4163
4253
  })),
4164
4254
  client.system.getSettings().catch(() => ({}))
4165
4255
  ]);
4166
- const jobsSummary = "jobs" in jobsResult ? String(jobsResult.jobs.length) : `unavailable (${jobsResult.error})`;
4256
+ const jobsSummary = "jobs" in jobsResult ? `${jobsResult.jobs.length}${"errors" in jobsResult && jobsResult.errors.length ? ` (${jobsResult.errors.map((error) => `${error.kind}: ${error.message}`).join("; ")})` : ""}` : `unavailable (${jobsResult.error})`;
4167
4257
  emit([
4168
4258
  "Session stats:",
4169
4259
  `- messages: ${messageCount}`,
@@ -4178,20 +4268,22 @@ async function handleDiagnosticCommand(slash, host, emit, readPresentation) {
4178
4268
  if (slash.name === "/agents") {
4179
4269
  if (!sessionId)
4180
4270
  return "handled";
4181
- const agents = await client.jobs.list({
4271
+ const listed = await client.jobs.list({
4182
4272
  sessionId,
4183
4273
  kinds: ["agent"],
4184
4274
  includeFinished: true,
4185
4275
  limit: 100
4186
4276
  });
4187
- if (agents.length === 0) {
4277
+ const agents = listed.jobs;
4278
+ if (agents.length === 0 && !listed.errors.length) {
4188
4279
  emit("No Agent Jobs.");
4189
4280
  return "handled";
4190
4281
  }
4191
4282
  emit([
4192
4283
  `Agent Jobs (${agents.length}):`,
4193
4284
  "",
4194
- ...agents.map((job) => ` ${job.id} [${job.status}] ${job.label}`)
4285
+ ...agents.map((job) => ` ${job.id} [${job.status}] ${job.label}`),
4286
+ ...listed.errors.map((error) => `${error.kind}: ${error.message}`)
4195
4287
  ].join(`
4196
4288
  `));
4197
4289
  return "handled";
@@ -4201,11 +4293,11 @@ async function handleDiagnosticCommand(slash, host, emit, readPresentation) {
4201
4293
  client.auth.getStatus().catch(() => null),
4202
4294
  client.system.listMemory({ cwd }).catch(() => ({ directory: "(unavailable)", entries: [] })),
4203
4295
  sessionId ? client.system.getSessionMcp(sessionId).catch(() => []) : Promise.resolve([]),
4204
- sessionId ? client.jobs.list({ sessionId, includeFinished: true, limit: 100 }).then((jobs) => ({ jobs })).catch((error) => ({
4296
+ sessionId ? client.jobs.list({ sessionId, includeFinished: true, limit: 100 }).then((listed) => ({ jobs: listed.jobs, errors: listed.errors })).catch((error) => ({
4205
4297
  error: error instanceof Error ? error.message : String(error)
4206
4298
  })) : Promise.resolve({ jobs: [] })
4207
4299
  ]);
4208
- const jobsSummary = "jobs" in jobsResult ? String(jobsResult.jobs.length) : `unavailable (${jobsResult.error})`;
4300
+ const jobsSummary = "jobs" in jobsResult ? `${jobsResult.jobs.length}${"errors" in jobsResult && jobsResult.errors.length ? ` (${jobsResult.errors.map((error) => `${error.kind}: ${error.message}`).join("; ")})` : ""}` : `unavailable (${jobsResult.error})`;
4209
4301
  const bucket = sessionId ? clientState.buckets[sessionId] : undefined;
4210
4302
  const diagnostics = await host.getRuntimeDiagnostics?.();
4211
4303
  const runtime = diagnostics?.runtime ?? "(not provided by this host)";
@@ -4300,41 +4392,44 @@ async function handleJobCommand(slash, host, emit, readPresentation) {
4300
4392
  if (!sessionId)
4301
4393
  return "handled";
4302
4394
  const args = slash.args.trim();
4303
- const [sub, id, ...extra] = args.split(/\s+/).filter(Boolean);
4304
- if ((!sub || sub === "list") && !id) {
4395
+ const [sub, kind, id, runId, ...extra] = args.split(/\s+/).filter(Boolean);
4396
+ if ((!sub || sub === "list") && !kind) {
4305
4397
  await readPresentation(`jobs:${sessionId}`, "Jobs", async () => {
4306
- const jobs = await client.jobs.list({
4398
+ const listed = await client.jobs.list({
4307
4399
  sessionId,
4308
4400
  includeFinished: true,
4309
4401
  limit: 100
4310
4402
  });
4311
- if (jobs.length === 0)
4403
+ const { jobs, errors } = listed;
4404
+ if (jobs.length === 0 && errors.length === 0)
4312
4405
  return "No Jobs.";
4313
4406
  return [
4314
4407
  `Jobs (${jobs.length}):`,
4315
4408
  "",
4316
- ...jobs.map((job) => ` ${job.id} [${job.status}] ${job.kind}: ${job.label}`)
4409
+ ...jobs.map((job) => ` ${job.id} [${job.status}] ${job.kind}: ${job.label}${job.target.kind === "agent" ? ` runId=${job.target.runId}` : ""}`),
4410
+ ...errors.map((error) => `${error.kind}: ${error.message}`)
4317
4411
  ].join(`
4318
4412
  `);
4319
4413
  });
4320
4414
  return "handled";
4321
4415
  }
4322
- if (sub === "show" && id && extra.length === 0) {
4416
+ const target = kind === "agent" && id && runId ? { kind, jobId: id, runId } : ["shell", "terminal", "workflow", "dream"].includes(kind ?? "") && id && !runId ? { kind, jobId: id } : undefined;
4417
+ if (sub === "show" && target && extra.length === 0) {
4323
4418
  await readPresentation(`job:${sessionId}:${id}`, "Jobs", async () => {
4324
- const result = await client.jobs.read(id, { sessionId });
4419
+ const result = await client.jobs.read(target, { sessionId });
4325
4420
  return formatJobReadResult(result);
4326
4421
  });
4327
4422
  return "handled";
4328
4423
  }
4329
- if (sub === "cancel" && id && extra.length === 0) {
4330
- const snapshot = await client.jobs.cancel(id, {
4424
+ if (sub === "cancel" && target && extra.length === 0) {
4425
+ const snapshot = await client.jobs.cancel(target, {
4331
4426
  sessionId,
4332
4427
  reason: "Cancelled from slash command"
4333
4428
  });
4334
4429
  emit(`Job ${snapshot.id} status: ${snapshot.status}.`);
4335
4430
  return "handled";
4336
4431
  }
4337
- emit("Usage: /jobs [list | show ID | cancel ID]");
4432
+ emit("Usage: /jobs [list | show KIND ID [RUN_ID] | cancel KIND ID [RUN_ID]]");
4338
4433
  return "handled";
4339
4434
  }
4340
4435
 
@@ -5865,7 +5960,12 @@ async function executeTuiAction(action, ctx) {
5865
5960
  ctx.jobControlAbortRef.current = controller;
5866
5961
  let response;
5867
5962
  try {
5868
- response = await client.jobs.cancel(action.job_id, {
5963
+ response = await client.jobs.cancel((() => {
5964
+ const selected = ctx.jobStateRef.current.jobs.find((job) => job.id === action.job_id)?.target;
5965
+ if (!selected)
5966
+ throw new Error("\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u5F53\u524D\u4EFB\u52A1\u3002");
5967
+ return selected;
5968
+ })(), {
5869
5969
  sessionId: currentSessionId,
5870
5970
  reason: action.reason
5871
5971
  }, { signal: controller.signal });
@@ -5909,7 +6009,12 @@ async function executeTuiAction(action, ctx) {
5909
6009
  const controller = new AbortController;
5910
6010
  ctx.jobControlAbortRef.current = controller;
5911
6011
  try {
5912
- await client.jobs.send(action.job_id, {
6012
+ await client.jobs.send((() => {
6013
+ const selected = ctx.jobStateRef.current.jobs.find((job) => job.id === action.job_id)?.target;
6014
+ if (!selected)
6015
+ throw new Error("\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u5F53\u524D\u4EFB\u52A1\u3002");
6016
+ return selected;
6017
+ })(), {
5913
6018
  sessionId: currentSessionId,
5914
6019
  data: action.data
5915
6020
  }, { signal: controller.signal });
@@ -6142,9 +6247,9 @@ function useServerSync(config, onError) {
6142
6247
  });
6143
6248
  if (activeSessionIdRef.current !== sessionId || jobGenerationRef.current !== generation)
6144
6249
  return;
6145
- const validated = validateJobSnapshots(response, sessionId);
6146
- if (validated.error) {
6147
- const validationError = validated.error;
6250
+ const validated = validateJobSnapshots(response.jobs, sessionId);
6251
+ if (validated.error || response.errors.length) {
6252
+ const validationError = validated.error ?? response.errors.map((error) => `${error.kind}: ${error.message}`).join("; ");
6148
6253
  const now = Date.now();
6149
6254
  setJobState((current) => {
6150
6255
  const next = validated.jobs.length > 0 ? {
@@ -6193,7 +6298,10 @@ function useServerSync(config, onError) {
6193
6298
  jobDetailStateRef.current = loading;
6194
6299
  setJobDetailState(loading);
6195
6300
  try {
6196
- const response = await client.jobs.read(jobId, {
6301
+ const selected = jobStateRef.current.jobs.find((job) => job.id === jobId)?.target;
6302
+ if (!selected)
6303
+ throw new Error("\u8BF7\u5148\u9009\u62E9\u4E00\u4E2A\u5F53\u524D\u4EFB\u52A1\u3002");
6304
+ const response = await client.jobs.read(selected, {
6197
6305
  sessionId,
6198
6306
  signal: controller.signal
6199
6307
  });
@@ -10446,4 +10554,4 @@ try {
10446
10554
  process.exit(1);
10447
10555
  }
10448
10556
 
10449
- //# debugId=30CD0A6402EF364864756E2164756E21
10557
+ //# debugId=D9F985CEDC561F0364756E2164756E21