@akagilnc/pi-workflow-roles 0.1.5797 → 0.1.5805

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.
@@ -1425,18 +1425,29 @@ async function runEngineDetourOnce(input) {
1425
1425
  function isEngineDetourFailure(result) {
1426
1426
  return result.code !== 0 || result.stdout.trim() === "";
1427
1427
  }
1428
- function engineDetourFailureDiagnostic(result) {
1429
- if (result.stderr.trim().length > 0) return result.stderr;
1430
- if (result.stdout.trim() === "") return ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC;
1431
- const rows = result.stdout.split("\n");
1432
- let lastRow = "";
1433
- for (let index = rows.length - 1; index >= 0; index -= 1) {
1434
- if (rows[index].trim() !== "") {
1435
- lastRow = rows[index];
1436
- break;
1437
- }
1428
+ function engineDetourFailureSeatText(result) {
1429
+ const parts = [];
1430
+ if (result.stdout.length > 0) parts.push(result.stdout);
1431
+ if (result.stderr.length > 0) parts.push(result.stderr);
1432
+ let text = "";
1433
+ if (parts.length > 0) {
1434
+ text = parts[0];
1435
+ for (let index = 1; index < parts.length; index += 1) {
1436
+ if (!text.endsWith("\n")) text += "\n";
1437
+ text += parts[index];
1438
+ }
1439
+ }
1440
+ const append = (line2) => {
1441
+ if (text.length > 0 && !text.endsWith("\n")) text += "\n";
1442
+ text += line2;
1443
+ };
1444
+ if (result.code !== 0) {
1445
+ append(`\u52B3\u52A1\u5F15\u64CE\u4EE5 code ${result.code} \u9000\u51FA`);
1446
+ }
1447
+ if (result.stdout.trim() === "") {
1448
+ append(ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC);
1438
1449
  }
1439
- return `\u52B3\u52A1\u5F15\u64CE\u4EE5 code ${result.code} \u9000\u51FA\uFF1A${lastRow}`;
1450
+ return text.length > 0 ? text : ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC;
1440
1451
  }
1441
1452
  function engineNameFromEnv() {
1442
1453
  return normalizeEngineName(
@@ -21065,6 +21076,7 @@ init_engine_material();
21065
21076
  init_engine_detour();
21066
21077
  init_engine_detour_usage();
21067
21078
  init_run_terminal_artifacts();
21079
+ init_serialize_thrown_value();
21068
21080
  import { Type as Type18 } from "typebox";
21069
21081
  var engineDetourArgsSchema = Type18.Object(
21070
21082
  {
@@ -21085,6 +21097,27 @@ function isCallerCancellation(error, signal) {
21085
21097
  function asError(error, fallback) {
21086
21098
  return error instanceof Error ? error : new Error(String(error).trim() || fallback);
21087
21099
  }
21100
+ function engineFailureResult(input) {
21101
+ const details = {
21102
+ tool: ENGINE_DETOUR_TOOL_NAME,
21103
+ ...input.code === void 0 ? {} : { code: input.code },
21104
+ ...input.stdout === void 0 ? {} : { stdout: input.stdout },
21105
+ ...input.stderr === void 0 ? {} : { stderr: input.stderr },
21106
+ ...input.errorCode === void 0 ? {} : { errorCode: input.errorCode },
21107
+ ...input.ledgerError === void 0 ? {} : { ledgerError: input.ledgerError }
21108
+ };
21109
+ let text = input.text;
21110
+ if (input.ledgerError !== void 0 && input.ledgerError.length > 0) {
21111
+ if (text.length > 0 && !text.endsWith("\n")) text += "\n";
21112
+ text += input.ledgerError;
21113
+ }
21114
+ return {
21115
+ content: [{ type: "text", text }],
21116
+ details,
21117
+ // Identified detour failure is a tool error, not a seat-run death (#1213).
21118
+ isError: true
21119
+ };
21120
+ }
21088
21121
  function createEngineDetourToolDefinition(input) {
21089
21122
  const engineName = input.engineName;
21090
21123
  const engineModel = input.engineModel;
@@ -21098,13 +21131,6 @@ function createEngineDetourToolDefinition(input) {
21098
21131
  async execute(toolCallId, params, signal, _onUpdate, ctx) {
21099
21132
  const args = params;
21100
21133
  const argv = Array.isArray(args.argv) ? args.argv : [];
21101
- if (argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
21102
- input.fail(
21103
- new Error("\u52B3\u52A1\u5F15\u64CE argv \u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4"),
21104
- toolCallId,
21105
- ctx
21106
- );
21107
- }
21108
21134
  const sessionParent = ctx.sessionManager?.getSessionFile?.();
21109
21135
  const startedAt = Date.now();
21110
21136
  const runDirectory = typeof ctx.runDirectory === "string" && ctx.runDirectory.length > 0 ? ctx.runDirectory : void 0;
@@ -21125,25 +21151,21 @@ function createEngineDetourToolDefinition(input) {
21125
21151
  ...observed2.stdoutByteLength === void 0 ? {} : { stdoutByteLength: observed2.stdoutByteLength }
21126
21152
  });
21127
21153
  };
21128
- const failAfterLedger = (engineCause, observed2, aggregateMessage) => {
21154
+ const recordCallOrLedgerError = (observed2) => {
21129
21155
  try {
21130
21156
  recordCall(observed2);
21157
+ return void 0;
21131
21158
  } catch (recordError) {
21132
- input.fail(
21133
- new AggregateError(
21134
- [
21135
- engineCause,
21136
- asError(recordError, "engine detour usage ledger write failed")
21137
- ],
21138
- aggregateMessage,
21139
- { cause: engineCause }
21140
- ),
21141
- toolCallId,
21142
- ctx
21143
- );
21159
+ return serializeThrownValue(recordError);
21144
21160
  }
21145
- input.fail(engineCause, toolCallId, ctx);
21146
21161
  };
21162
+ if (argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
21163
+ const ledgerError = recordCallOrLedgerError({});
21164
+ return engineFailureResult({
21165
+ text: "\u52B3\u52A1\u5F15\u64CE argv \u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4",
21166
+ ...ledgerError === void 0 ? {} : { ledgerError }
21167
+ });
21168
+ }
21147
21169
  let result;
21148
21170
  try {
21149
21171
  result = await runEngineDetourOnce({
@@ -21153,20 +21175,26 @@ function createEngineDetourToolDefinition(input) {
21153
21175
  });
21154
21176
  } catch (error) {
21155
21177
  if (isCallerCancellation(error, signal)) throw error;
21156
- return failAfterLedger(
21157
- asError(error, "\u52B3\u52A1\u5F15\u64CE spawn \u5931\u8D25"),
21158
- {},
21159
- "engine detour spawn and usage ledger both failed"
21160
- );
21178
+ const engineError = asError(error, "\u52B3\u52A1\u5F15\u64CE spawn \u5931\u8D25");
21179
+ const ledgerError = recordCallOrLedgerError({});
21180
+ const errno = typeof engineError.code === "string" ? engineError.code : void 0;
21181
+ return engineFailureResult({
21182
+ text: engineError.message,
21183
+ ...errno === void 0 ? {} : { errorCode: errno },
21184
+ ...ledgerError === void 0 ? {} : { ledgerError }
21185
+ });
21161
21186
  }
21162
21187
  const stdoutByteLength = engineDetourStdoutByteLength(result.stdout);
21163
21188
  const observed = { code: result.code, stdoutByteLength };
21164
21189
  if (isEngineDetourFailure(result)) {
21165
- return failAfterLedger(
21166
- new Error(engineDetourFailureDiagnostic(result)),
21167
- observed,
21168
- "engine detour child-close and usage ledger both failed"
21169
- );
21190
+ const ledgerError = recordCallOrLedgerError(observed);
21191
+ return engineFailureResult({
21192
+ text: engineDetourFailureSeatText(result),
21193
+ code: result.code,
21194
+ stdout: result.stdout,
21195
+ stderr: result.stderr,
21196
+ ...ledgerError === void 0 ? {} : { ledgerError }
21197
+ });
21170
21198
  }
21171
21199
  recordCall(observed);
21172
21200
  return {
@@ -21180,7 +21208,7 @@ function createEngineDetourToolDefinition(input) {
21180
21208
  }
21181
21209
  };
21182
21210
  }
21183
- function registerEngineDetourTool(roleHost, hostActions) {
21211
+ function registerEngineDetourTool(roleHost) {
21184
21212
  const getFlag = (name) => roleHost.getFlag(name);
21185
21213
  const engineName = resolveEngineName(getFlag);
21186
21214
  if (engineName === void 0) {
@@ -21189,10 +21217,7 @@ function registerEngineDetourTool(roleHost, hostActions) {
21189
21217
  const engineModel = resolveEngineModel(getFlag);
21190
21218
  const definition = createEngineDetourToolDefinition({
21191
21219
  engineName,
21192
- ...engineModel === void 0 ? {} : { engineModel },
21193
- fail(error, toolCallId, ctx) {
21194
- hostActions.failInfrastructure(error, ctx, toolCallId);
21195
- }
21220
+ ...engineModel === void 0 ? {} : { engineModel }
21196
21221
  });
21197
21222
  roleHost.registerTool(definition);
21198
21223
  return true;
@@ -24128,7 +24153,7 @@ function createRoleRuntimeExtension(dependencies, deliveryRequestLimit) {
24128
24153
  ]);
24129
24154
  roleReferenceMaterials = await dependencies.loadRoleReferenceMaterials?.(entry.role) ?? "";
24130
24155
  if (!engineDetourRegistered) {
24131
- engineDetourRegistered = registerEngineDetourTool(roleHost, hostActions);
24156
+ engineDetourRegistered = registerEngineDetourTool(roleHost);
24132
24157
  }
24133
24158
  if (secretariat.claimsEngineDetour === true && engineDetourRegistered) {
24134
24159
  roleHost.setActiveTools([
@@ -24501,7 +24526,7 @@ async function prepareRoleEnvelope(options) {
24501
24526
  const projected = await projectToolResult(toolCallId, name, {
24502
24527
  content: result.content,
24503
24528
  details: result.details,
24504
- isError: false
24529
+ isError: result.isError === true
24505
24530
  });
24506
24531
  if (result.terminate === false) {
24507
24532
  rejection = {
@@ -1,7 +1,7 @@
1
1
  import { Type } from "typebox";
2
2
  import {
3
3
  ENGINE_DETOUR_TOOL_NAME,
4
- engineDetourFailureDiagnostic,
4
+ engineDetourFailureSeatText,
5
5
  isEngineDetourFailure,
6
6
  resolveEngineModel,
7
7
  resolveEngineName,
@@ -12,6 +12,7 @@ import {
12
12
  reportEngineDetourCall
13
13
  } from "./engine-detour-usage.js";
14
14
  import { runIdFromRunDirectory } from "./run-terminal-artifacts.js";
15
+ import { serializeThrownValue } from "./serialize-thrown-value.js";
15
16
  const engineDetourArgsSchema = Type.Object(
16
17
  {
17
18
  argv: Type.Array(Type.String({ minLength: 1 }), {
@@ -31,6 +32,27 @@ function isCallerCancellation(error, signal) {
31
32
  function asError(error, fallback) {
32
33
  return error instanceof Error ? error : new Error(String(error).trim() || fallback);
33
34
  }
35
+ function engineFailureResult(input) {
36
+ const details = {
37
+ tool: ENGINE_DETOUR_TOOL_NAME,
38
+ ...input.code === void 0 ? {} : { code: input.code },
39
+ ...input.stdout === void 0 ? {} : { stdout: input.stdout },
40
+ ...input.stderr === void 0 ? {} : { stderr: input.stderr },
41
+ ...input.errorCode === void 0 ? {} : { errorCode: input.errorCode },
42
+ ...input.ledgerError === void 0 ? {} : { ledgerError: input.ledgerError }
43
+ };
44
+ let text = input.text;
45
+ if (input.ledgerError !== void 0 && input.ledgerError.length > 0) {
46
+ if (text.length > 0 && !text.endsWith("\n")) text += "\n";
47
+ text += input.ledgerError;
48
+ }
49
+ return {
50
+ content: [{ type: "text", text }],
51
+ details,
52
+ // Identified detour failure is a tool error, not a seat-run death (#1213).
53
+ isError: true
54
+ };
55
+ }
34
56
  function createEngineDetourToolDefinition(input) {
35
57
  const engineName = input.engineName;
36
58
  const engineModel = input.engineModel;
@@ -44,13 +66,6 @@ function createEngineDetourToolDefinition(input) {
44
66
  async execute(toolCallId, params, signal, _onUpdate, ctx) {
45
67
  const args = params;
46
68
  const argv = Array.isArray(args.argv) ? args.argv : [];
47
- if (argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
48
- input.fail(
49
- new Error("\u52B3\u52A1\u5F15\u64CE argv \u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4"),
50
- toolCallId,
51
- ctx
52
- );
53
- }
54
69
  const sessionParent = ctx.sessionManager?.getSessionFile?.();
55
70
  const startedAt = Date.now();
56
71
  const runDirectory = typeof ctx.runDirectory === "string" && ctx.runDirectory.length > 0 ? ctx.runDirectory : void 0;
@@ -71,25 +86,21 @@ function createEngineDetourToolDefinition(input) {
71
86
  ...observed2.stdoutByteLength === void 0 ? {} : { stdoutByteLength: observed2.stdoutByteLength }
72
87
  });
73
88
  };
74
- const failAfterLedger = (engineCause, observed2, aggregateMessage) => {
89
+ const recordCallOrLedgerError = (observed2) => {
75
90
  try {
76
91
  recordCall(observed2);
92
+ return void 0;
77
93
  } catch (recordError) {
78
- input.fail(
79
- new AggregateError(
80
- [
81
- engineCause,
82
- asError(recordError, "engine detour usage ledger write failed")
83
- ],
84
- aggregateMessage,
85
- { cause: engineCause }
86
- ),
87
- toolCallId,
88
- ctx
89
- );
94
+ return serializeThrownValue(recordError);
90
95
  }
91
- input.fail(engineCause, toolCallId, ctx);
92
96
  };
97
+ if (argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
98
+ const ledgerError = recordCallOrLedgerError({});
99
+ return engineFailureResult({
100
+ text: "\u52B3\u52A1\u5F15\u64CE argv \u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4",
101
+ ...ledgerError === void 0 ? {} : { ledgerError }
102
+ });
103
+ }
93
104
  let result;
94
105
  try {
95
106
  result = await runEngineDetourOnce({
@@ -99,20 +110,26 @@ function createEngineDetourToolDefinition(input) {
99
110
  });
100
111
  } catch (error) {
101
112
  if (isCallerCancellation(error, signal)) throw error;
102
- return failAfterLedger(
103
- asError(error, "\u52B3\u52A1\u5F15\u64CE spawn \u5931\u8D25"),
104
- {},
105
- "engine detour spawn and usage ledger both failed"
106
- );
113
+ const engineError = asError(error, "\u52B3\u52A1\u5F15\u64CE spawn \u5931\u8D25");
114
+ const ledgerError = recordCallOrLedgerError({});
115
+ const errno = typeof engineError.code === "string" ? engineError.code : void 0;
116
+ return engineFailureResult({
117
+ text: engineError.message,
118
+ ...errno === void 0 ? {} : { errorCode: errno },
119
+ ...ledgerError === void 0 ? {} : { ledgerError }
120
+ });
107
121
  }
108
122
  const stdoutByteLength = engineDetourStdoutByteLength(result.stdout);
109
123
  const observed = { code: result.code, stdoutByteLength };
110
124
  if (isEngineDetourFailure(result)) {
111
- return failAfterLedger(
112
- new Error(engineDetourFailureDiagnostic(result)),
113
- observed,
114
- "engine detour child-close and usage ledger both failed"
115
- );
125
+ const ledgerError = recordCallOrLedgerError(observed);
126
+ return engineFailureResult({
127
+ text: engineDetourFailureSeatText(result),
128
+ code: result.code,
129
+ stdout: result.stdout,
130
+ stderr: result.stderr,
131
+ ...ledgerError === void 0 ? {} : { ledgerError }
132
+ });
116
133
  }
117
134
  recordCall(observed);
118
135
  return {
@@ -126,7 +143,7 @@ function createEngineDetourToolDefinition(input) {
126
143
  }
127
144
  };
128
145
  }
129
- function registerEngineDetourTool(roleHost, hostActions) {
146
+ function registerEngineDetourTool(roleHost) {
130
147
  const getFlag = (name) => roleHost.getFlag(name);
131
148
  const engineName = resolveEngineName(getFlag);
132
149
  if (engineName === void 0) {
@@ -135,10 +152,7 @@ function registerEngineDetourTool(roleHost, hostActions) {
135
152
  const engineModel = resolveEngineModel(getFlag);
136
153
  const definition = createEngineDetourToolDefinition({
137
154
  engineName,
138
- ...engineModel === void 0 ? {} : { engineModel },
139
- fail(error, toolCallId, ctx) {
140
- hostActions.failInfrastructure(error, ctx, toolCallId);
141
- }
155
+ ...engineModel === void 0 ? {} : { engineModel }
142
156
  });
143
157
  roleHost.registerTool(definition);
144
158
  return true;
@@ -89,18 +89,29 @@ async function runEngineDetourOnce(input) {
89
89
  function isEngineDetourFailure(result) {
90
90
  return result.code !== 0 || result.stdout.trim() === "";
91
91
  }
92
- function engineDetourFailureDiagnostic(result) {
93
- if (result.stderr.trim().length > 0) return result.stderr;
94
- if (result.stdout.trim() === "") return ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC;
95
- const rows = result.stdout.split("\n");
96
- let lastRow = "";
97
- for (let index = rows.length - 1; index >= 0; index -= 1) {
98
- if (rows[index].trim() !== "") {
99
- lastRow = rows[index];
100
- break;
92
+ function engineDetourFailureSeatText(result) {
93
+ const parts = [];
94
+ if (result.stdout.length > 0) parts.push(result.stdout);
95
+ if (result.stderr.length > 0) parts.push(result.stderr);
96
+ let text = "";
97
+ if (parts.length > 0) {
98
+ text = parts[0];
99
+ for (let index = 1; index < parts.length; index += 1) {
100
+ if (!text.endsWith("\n")) text += "\n";
101
+ text += parts[index];
101
102
  }
102
103
  }
103
- return `\u52B3\u52A1\u5F15\u64CE\u4EE5 code ${result.code} \u9000\u51FA\uFF1A${lastRow}`;
104
+ const append = (line) => {
105
+ if (text.length > 0 && !text.endsWith("\n")) text += "\n";
106
+ text += line;
107
+ };
108
+ if (result.code !== 0) {
109
+ append(`\u52B3\u52A1\u5F15\u64CE\u4EE5 code ${result.code} \u9000\u51FA`);
110
+ }
111
+ if (result.stdout.trim() === "") {
112
+ append(ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC);
113
+ }
114
+ return text.length > 0 ? text : ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC;
104
115
  }
105
116
  function engineNameFromEnv() {
106
117
  return normalizeEngineName(
@@ -127,7 +138,7 @@ export {
127
138
  ENGINE_FLAG_NAME,
128
139
  ENGINE_MODEL_FLAG_NAME,
129
140
  applyEngineChildEnv,
130
- engineDetourFailureDiagnostic,
141
+ engineDetourFailureSeatText,
131
142
  engineNameFromEnv,
132
143
  isEngineDetourFailure,
133
144
  normalizeEngineName,
@@ -5808,18 +5808,29 @@ async function runEngineDetourOnce(input) {
5808
5808
  function isEngineDetourFailure(result) {
5809
5809
  return result.code !== 0 || result.stdout.trim() === "";
5810
5810
  }
5811
- function engineDetourFailureDiagnostic(result) {
5812
- if (result.stderr.trim().length > 0) return result.stderr;
5813
- if (result.stdout.trim() === "") return ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC;
5814
- const rows = result.stdout.split("\n");
5815
- let lastRow = "";
5816
- for (let index = rows.length - 1; index >= 0; index -= 1) {
5817
- if (rows[index].trim() !== "") {
5818
- lastRow = rows[index];
5819
- break;
5820
- }
5811
+ function engineDetourFailureSeatText(result) {
5812
+ const parts = [];
5813
+ if (result.stdout.length > 0) parts.push(result.stdout);
5814
+ if (result.stderr.length > 0) parts.push(result.stderr);
5815
+ let text = "";
5816
+ if (parts.length > 0) {
5817
+ text = parts[0];
5818
+ for (let index = 1; index < parts.length; index += 1) {
5819
+ if (!text.endsWith("\n")) text += "\n";
5820
+ text += parts[index];
5821
+ }
5822
+ }
5823
+ const append = (line2) => {
5824
+ if (text.length > 0 && !text.endsWith("\n")) text += "\n";
5825
+ text += line2;
5826
+ };
5827
+ if (result.code !== 0) {
5828
+ append(`\u52B3\u52A1\u5F15\u64CE\u4EE5 code ${result.code} \u9000\u51FA`);
5829
+ }
5830
+ if (result.stdout.trim() === "") {
5831
+ append(ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC);
5821
5832
  }
5822
- return `\u52B3\u52A1\u5F15\u64CE\u4EE5 code ${result.code} \u9000\u51FA\uFF1A${lastRow}`;
5833
+ return text.length > 0 ? text : ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC;
5823
5834
  }
5824
5835
  function engineNameFromEnv() {
5825
5836
  return normalizeEngineName(
@@ -21875,6 +21886,7 @@ init_engine_material();
21875
21886
  init_engine_detour();
21876
21887
  init_engine_detour_usage();
21877
21888
  init_run_terminal_artifacts();
21889
+ init_serialize_thrown_value();
21878
21890
  import { Type as Type19 } from "typebox";
21879
21891
  var engineDetourArgsSchema = Type19.Object(
21880
21892
  {
@@ -21895,6 +21907,27 @@ function isCallerCancellation(error, signal) {
21895
21907
  function asError(error, fallback) {
21896
21908
  return error instanceof Error ? error : new Error(String(error).trim() || fallback);
21897
21909
  }
21910
+ function engineFailureResult(input) {
21911
+ const details = {
21912
+ tool: ENGINE_DETOUR_TOOL_NAME,
21913
+ ...input.code === void 0 ? {} : { code: input.code },
21914
+ ...input.stdout === void 0 ? {} : { stdout: input.stdout },
21915
+ ...input.stderr === void 0 ? {} : { stderr: input.stderr },
21916
+ ...input.errorCode === void 0 ? {} : { errorCode: input.errorCode },
21917
+ ...input.ledgerError === void 0 ? {} : { ledgerError: input.ledgerError }
21918
+ };
21919
+ let text = input.text;
21920
+ if (input.ledgerError !== void 0 && input.ledgerError.length > 0) {
21921
+ if (text.length > 0 && !text.endsWith("\n")) text += "\n";
21922
+ text += input.ledgerError;
21923
+ }
21924
+ return {
21925
+ content: [{ type: "text", text }],
21926
+ details,
21927
+ // Identified detour failure is a tool error, not a seat-run death (#1213).
21928
+ isError: true
21929
+ };
21930
+ }
21898
21931
  function createEngineDetourToolDefinition(input) {
21899
21932
  const engineName = input.engineName;
21900
21933
  const engineModel = input.engineModel;
@@ -21908,13 +21941,6 @@ function createEngineDetourToolDefinition(input) {
21908
21941
  async execute(toolCallId, params, signal, _onUpdate, ctx) {
21909
21942
  const args = params;
21910
21943
  const argv = Array.isArray(args.argv) ? args.argv : [];
21911
- if (argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
21912
- input.fail(
21913
- new Error("\u52B3\u52A1\u5F15\u64CE argv \u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4"),
21914
- toolCallId,
21915
- ctx
21916
- );
21917
- }
21918
21944
  const sessionParent = ctx.sessionManager?.getSessionFile?.();
21919
21945
  const startedAt = Date.now();
21920
21946
  const runDirectory = typeof ctx.runDirectory === "string" && ctx.runDirectory.length > 0 ? ctx.runDirectory : void 0;
@@ -21935,25 +21961,21 @@ function createEngineDetourToolDefinition(input) {
21935
21961
  ...observed2.stdoutByteLength === void 0 ? {} : { stdoutByteLength: observed2.stdoutByteLength }
21936
21962
  });
21937
21963
  };
21938
- const failAfterLedger = (engineCause, observed2, aggregateMessage) => {
21964
+ const recordCallOrLedgerError = (observed2) => {
21939
21965
  try {
21940
21966
  recordCall(observed2);
21967
+ return void 0;
21941
21968
  } catch (recordError) {
21942
- input.fail(
21943
- new AggregateError(
21944
- [
21945
- engineCause,
21946
- asError(recordError, "engine detour usage ledger write failed")
21947
- ],
21948
- aggregateMessage,
21949
- { cause: engineCause }
21950
- ),
21951
- toolCallId,
21952
- ctx
21953
- );
21969
+ return serializeThrownValue(recordError);
21954
21970
  }
21955
- input.fail(engineCause, toolCallId, ctx);
21956
21971
  };
21972
+ if (argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
21973
+ const ledgerError = recordCallOrLedgerError({});
21974
+ return engineFailureResult({
21975
+ text: "\u52B3\u52A1\u5F15\u64CE argv \u987B\u4E3A\u975E\u7A7A\u5B57\u7B26\u4E32\u6570\u7EC4",
21976
+ ...ledgerError === void 0 ? {} : { ledgerError }
21977
+ });
21978
+ }
21957
21979
  let result;
21958
21980
  try {
21959
21981
  result = await runEngineDetourOnce({
@@ -21963,20 +21985,26 @@ function createEngineDetourToolDefinition(input) {
21963
21985
  });
21964
21986
  } catch (error) {
21965
21987
  if (isCallerCancellation(error, signal)) throw error;
21966
- return failAfterLedger(
21967
- asError(error, "\u52B3\u52A1\u5F15\u64CE spawn \u5931\u8D25"),
21968
- {},
21969
- "engine detour spawn and usage ledger both failed"
21970
- );
21988
+ const engineError = asError(error, "\u52B3\u52A1\u5F15\u64CE spawn \u5931\u8D25");
21989
+ const ledgerError = recordCallOrLedgerError({});
21990
+ const errno = typeof engineError.code === "string" ? engineError.code : void 0;
21991
+ return engineFailureResult({
21992
+ text: engineError.message,
21993
+ ...errno === void 0 ? {} : { errorCode: errno },
21994
+ ...ledgerError === void 0 ? {} : { ledgerError }
21995
+ });
21971
21996
  }
21972
21997
  const stdoutByteLength = engineDetourStdoutByteLength(result.stdout);
21973
21998
  const observed = { code: result.code, stdoutByteLength };
21974
21999
  if (isEngineDetourFailure(result)) {
21975
- return failAfterLedger(
21976
- new Error(engineDetourFailureDiagnostic(result)),
21977
- observed,
21978
- "engine detour child-close and usage ledger both failed"
21979
- );
22000
+ const ledgerError = recordCallOrLedgerError(observed);
22001
+ return engineFailureResult({
22002
+ text: engineDetourFailureSeatText(result),
22003
+ code: result.code,
22004
+ stdout: result.stdout,
22005
+ stderr: result.stderr,
22006
+ ...ledgerError === void 0 ? {} : { ledgerError }
22007
+ });
21980
22008
  }
21981
22009
  recordCall(observed);
21982
22010
  return {
@@ -21990,7 +22018,7 @@ function createEngineDetourToolDefinition(input) {
21990
22018
  }
21991
22019
  };
21992
22020
  }
21993
- function registerEngineDetourTool(roleHost, hostActions) {
22021
+ function registerEngineDetourTool(roleHost) {
21994
22022
  const getFlag = (name) => roleHost.getFlag(name);
21995
22023
  const engineName = resolveEngineName(getFlag);
21996
22024
  if (engineName === void 0) {
@@ -21999,10 +22027,7 @@ function registerEngineDetourTool(roleHost, hostActions) {
21999
22027
  const engineModel = resolveEngineModel(getFlag);
22000
22028
  const definition = createEngineDetourToolDefinition({
22001
22029
  engineName,
22002
- ...engineModel === void 0 ? {} : { engineModel },
22003
- fail(error, toolCallId, ctx) {
22004
- hostActions.failInfrastructure(error, ctx, toolCallId);
22005
- }
22030
+ ...engineModel === void 0 ? {} : { engineModel }
22006
22031
  });
22007
22032
  roleHost.registerTool(definition);
22008
22033
  return true;
@@ -24148,7 +24173,7 @@ function createRoleRuntimeExtension(dependencies, deliveryRequestLimit) {
24148
24173
  ]);
24149
24174
  roleReferenceMaterials = await dependencies.loadRoleReferenceMaterials?.(entry.role) ?? "";
24150
24175
  if (!engineDetourRegistered) {
24151
- engineDetourRegistered = registerEngineDetourTool(roleHost, hostActions);
24176
+ engineDetourRegistered = registerEngineDetourTool(roleHost);
24152
24177
  }
24153
24178
  if (secretariat.claimsEngineDetour === true && engineDetourRegistered) {
24154
24179
  roleHost.setActiveTools([
@@ -24572,7 +24597,7 @@ async function prepareRoleEnvelope(options) {
24572
24597
  const projected = await projectToolResult(toolCallId, name, {
24573
24598
  content: result.content,
24574
24599
  details: result.details,
24575
- isError: false
24600
+ isError: result.isError === true
24576
24601
  });
24577
24602
  if (result.terminate === false) {
24578
24603
  rejection = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akagilnc/pi-workflow-roles",
3
- "version": "0.1.5797",
3
+ "version": "0.1.5805",
4
4
  "description": "Soul-bound workflow roles for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -45,9 +45,9 @@ themselves. Stuffing large bodies into argv/prompt is the verified cause of
45
45
 
46
46
  ## Failure handling
47
47
 
48
- Once an engine is selected, invoking that engine CLI is mandatory: you MUST
49
- actually run it. On any spawn, auth, quota, model-id, stream-stall,
50
- connection-drop, or other engine-process failure, return the typed failure and
51
- STOP — the run fails. In-seat labor after an engine-process failure is
52
- FORBIDDEN, and so is skipping the engine CLI to work in-seat. Zero invocations
53
- is a violation, not a fallback. Do not silently swap to another engine id.
48
+ Prefer outsourcing labor to the selected engine rather than doing the work
49
+ in-seat (尽量外包,别自己干). On spawn, auth, quota, model-id, stream-stall,
50
+ connection-drop, or other engine-process failure, the real failure and any
51
+ process result come back to this same session; decide next steps here (retry,
52
+ report, or submit through the existing path). Do not silently swap to another
53
+ engine id.
@@ -35,6 +35,6 @@ agy --sandbox --dangerously-skip-permissions --print 'YOUR_LABOR_PROMPT' --log-f
35
35
  ```
36
36
 
37
37
  When the dispatch order names a model, pass it verbatim via `--model`; an
38
- unknown model id is an engine-process failure (typed failure, stop — per
38
+ unknown model id is an engine-process failure (returned to this seat — per
39
39
  `../engine-dispatch.md`). Never copy a model id from this note or from a
40
40
  previous run — ids here would go stale.
@@ -1,8 +1,9 @@
1
1
  /**
2
- * Package-owned engine detour tool (#357 T2 / #378 / #380).
2
+ * Package-owned engine detour tool (#357 T2 / #378 / #380 / #1213).
3
3
  * Registered by shared role-runtime when any role + engine activation signal is present.
4
4
  * Evidence-child legs install the same definition via customTools (no spawn in role modules).
5
- * Engine process failures stop through the host infrastructure-failure seam.
5
+ * Engine process failures return as tool results to the same seat session — the package
6
+ * does not abort the seat run on their behalf (#1213).
6
7
  * Caller AbortSignal cancellation propagates unchanged.
7
8
  */
8
9
  import { Type, type Static } from "typebox";
@@ -10,7 +11,7 @@ import type { HostContext, HostToolDefinition, HostToolResult, RoleHost } from "
10
11
 
11
12
  import {
12
13
  ENGINE_DETOUR_TOOL_NAME,
13
- engineDetourFailureDiagnostic,
14
+ engineDetourFailureSeatText,
14
15
  isEngineDetourFailure,
15
16
  resolveEngineModel,
16
17
  resolveEngineName,
@@ -21,6 +22,7 @@ import {
21
22
  reportEngineDetourCall,
22
23
  } from "./engine-detour-usage.ts";
23
24
  import { runIdFromRunDirectory } from "./run-terminal-artifacts.ts";
25
+ import { serializeThrownValue } from "./serialize-thrown-value.ts";
24
26
 
25
27
  // #836 r16 class 3: argv required/minItems/element-minLength stay — execute()
26
28
  // must obtain the first item as the executable and spawn it (below; #82-98).
@@ -46,14 +48,6 @@ type EngineDetourContext = Pick<HostContext, "cwd" | "mode" | "abort"> & {
46
48
  host?: string;
47
49
  };
48
50
 
49
- export type EngineDetourHostActions = {
50
- failInfrastructure(
51
- error: unknown,
52
- ctx: EngineDetourContext,
53
- toolCallId?: string,
54
- ): never;
55
- };
56
-
57
51
  /** Caller/upper-layer cancellation must propagate unchanged. */
58
52
  function isCallerCancellation(
59
53
  error: unknown,
@@ -76,17 +70,60 @@ function asError(error: unknown, fallback: string): Error {
76
70
  : new Error(String(error).trim() || fallback);
77
71
  }
78
72
 
73
+ /** Structured details shared by success and engine-process failure results. */
74
+ type EngineDetourResultDetails = {
75
+ tool: typeof ENGINE_DETOUR_TOOL_NAME;
76
+ code?: number;
77
+ /** Full child stdout when the process closed (failure keeps labor output). */
78
+ stdout?: string;
79
+ stderr?: string;
80
+ /** Node errno on spawn failure (e.g. ENOENT). */
81
+ errorCode?: string;
82
+ /** Full ledger-write failure when dual-fail (serializeThrownValue; seat still lives). */
83
+ ledgerError?: string;
84
+ };
85
+
86
+ function engineFailureResult(input: {
87
+ text: string;
88
+ code?: number;
89
+ stdout?: string;
90
+ stderr?: string;
91
+ errorCode?: string;
92
+ ledgerError?: string;
93
+ }): HostToolResult<EngineDetourResultDetails> {
94
+ const details: EngineDetourResultDetails = {
95
+ tool: ENGINE_DETOUR_TOOL_NAME,
96
+ ...(input.code === undefined ? {} : { code: input.code }),
97
+ ...(input.stdout === undefined ? {} : { stdout: input.stdout }),
98
+ ...(input.stderr === undefined ? {} : { stderr: input.stderr }),
99
+ ...(input.errorCode === undefined ? {} : { errorCode: input.errorCode }),
100
+ ...(input.ledgerError === undefined ? {} : { ledgerError: input.ledgerError }),
101
+ };
102
+ // MCP / invokeAkTool deliver content only — dual-fail ledger cause must ride
103
+ // the seat-visible content channel, not details alone (#1213).
104
+ let text = input.text;
105
+ if (input.ledgerError !== undefined && input.ledgerError.length > 0) {
106
+ if (text.length > 0 && !text.endsWith("\n")) text += "\n";
107
+ text += input.ledgerError;
108
+ }
109
+ return {
110
+ content: [{ type: "text" as const, text }],
111
+ details,
112
+ // Identified detour failure is a tool error, not a seat-run death (#1213).
113
+ isError: true as const,
114
+ };
115
+ }
116
+
79
117
  /**
80
118
  * Build one detour tool definition for a configured engine name.
81
- * `fail` owns host abort (parent) vs throw (evidence child) for tool misuse only.
82
- * Engine process failures (nonzero/empty/spawn) stop via `fail` with their cause.
119
+ * Engine process failures (nonzero/empty/spawn) and empty/invalid argv return as
120
+ * ordinary tool results to the same seat — no seat-run abort (#1213 A3).
83
121
  * Caller AbortSignal cancellation propagates unchanged.
84
122
  */
85
123
  export function createEngineDetourToolDefinition(input: {
86
124
  engineName: string;
87
125
  /** Optional pool-directive model id (#883); description only — argv stays caller-assembled. */
88
126
  engineModel?: string;
89
- fail: (error: Error, toolCallId: string, ctx: EngineDetourContext) => never;
90
127
  }): HostToolDefinition<typeof engineDetourArgsSchema, unknown, EngineDetourContext> {
91
128
  const engineName = input.engineName;
92
129
  const engineModel = input.engineModel;
@@ -110,13 +147,6 @@ export function createEngineDetourToolDefinition(input: {
110
147
  ): Promise<HostToolResult> {
111
148
  const args = params as EngineDetourArgs;
112
149
  const argv = Array.isArray(args.argv) ? args.argv : [];
113
- if (argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
114
- input.fail(
115
- new Error("劳务引擎 argv 须为非空字符串数组"),
116
- toolCallId,
117
- ctx,
118
- );
119
- }
120
150
 
121
151
  const sessionParent = ctx.sessionManager?.getSessionFile?.();
122
152
  const startedAt = Date.now();
@@ -155,31 +185,28 @@ export function createEngineDetourToolDefinition(input: {
155
185
  });
156
186
  };
157
187
 
158
- /** Preserve engine cause; if ledger write also fails, keep both (失败诚实). */
159
- const failAfterLedger = (
160
- engineCause: Error,
188
+ /** Record usage; on ledger failure keep the full thrown value (失败诚实, no abort). */
189
+ const recordCallOrLedgerError = (
161
190
  observed: { code?: number; stdoutByteLength?: number },
162
- aggregateMessage: string,
163
- ): never => {
191
+ ): string | undefined => {
164
192
  try {
165
193
  recordCall(observed);
194
+ return undefined;
166
195
  } catch (recordError) {
167
- input.fail(
168
- new AggregateError(
169
- [
170
- engineCause,
171
- asError(recordError, "engine detour usage ledger write failed"),
172
- ],
173
- aggregateMessage,
174
- { cause: engineCause },
175
- ),
176
- toolCallId,
177
- ctx,
178
- );
196
+ return serializeThrownValue(recordError);
179
197
  }
180
- input.fail(engineCause, toolCallId, ctx);
181
198
  };
182
199
 
200
+ if (argv.length === 0 || argv.some((part) => typeof part !== "string" || part.length === 0)) {
201
+ // A3: parameter error returns to the seat — do not abort the run.
202
+ // Call actually entered execute: book it with no forged process metrics (#1213 / #537).
203
+ const ledgerError = recordCallOrLedgerError({});
204
+ return engineFailureResult({
205
+ text: "劳务引擎 argv 须为非空字符串数组",
206
+ ...(ledgerError === undefined ? {} : { ledgerError }),
207
+ });
208
+ }
209
+
183
210
  let result: Awaited<ReturnType<typeof runEngineDetourOnce>>;
184
211
  try {
185
212
  result = await runEngineDetourOnce({
@@ -190,24 +217,34 @@ export function createEngineDetourToolDefinition(input: {
190
217
  } catch (error) {
191
218
  if (isCallerCancellation(error, signal)) throw error;
192
219
  // Spawn path: duration only — code/stdout bytes absent (not forged 0).
193
- return failAfterLedger(
194
- asError(error, "劳务引擎 spawn 失败"),
195
- {},
196
- "engine detour spawn and usage ledger both failed",
197
- );
220
+ // Return the real cause to the seat; do not abort the run (#1213).
221
+ const engineError = asError(error, "劳务引擎 spawn 失败");
222
+ const ledgerError = recordCallOrLedgerError({});
223
+ const errno =
224
+ typeof (engineError as NodeJS.ErrnoException).code === "string"
225
+ ? (engineError as NodeJS.ErrnoException).code
226
+ : undefined;
227
+ return engineFailureResult({
228
+ text: engineError.message,
229
+ ...(errno === undefined ? {} : { errorCode: errno }),
230
+ ...(ledgerError === undefined ? {} : { ledgerError }),
231
+ });
198
232
  }
199
233
 
200
234
  const stdoutByteLength = engineDetourStdoutByteLength(result.stdout);
201
235
  const observed = { code: result.code, stdoutByteLength };
202
236
 
203
237
  // Classify closed-child failure before ledger write so a sitian failure
204
- // cannot erase nonzero/empty engine facts.
238
+ // cannot erase nonzero/empty engine facts. Still a tool result (#1213).
205
239
  if (isEngineDetourFailure(result)) {
206
- return failAfterLedger(
207
- new Error(engineDetourFailureDiagnostic(result)),
208
- observed,
209
- "engine detour child-close and usage ledger both failed",
210
- );
240
+ const ledgerError = recordCallOrLedgerError(observed);
241
+ return engineFailureResult({
242
+ text: engineDetourFailureSeatText(result),
243
+ code: result.code,
244
+ stdout: result.stdout,
245
+ stderr: result.stderr,
246
+ ...(ledgerError === undefined ? {} : { ledgerError }),
247
+ });
211
248
  }
212
249
 
213
250
  recordCall(observed);
@@ -230,10 +267,7 @@ export function createEngineDetourToolDefinition(input: {
230
267
  * activation signal. Signal is request-scoped via RoleHost flag, with pi
231
268
  * child-process env as fallback (resolveEngineName). Returns whether registration occurred.
232
269
  */
233
- export function registerEngineDetourTool(
234
- roleHost: RoleHost,
235
- hostActions: EngineDetourHostActions,
236
- ): boolean {
270
+ export function registerEngineDetourTool(roleHost: RoleHost): boolean {
237
271
  const getFlag = (name: string) => roleHost.getFlag(name);
238
272
  const engineName = resolveEngineName(getFlag);
239
273
  if (engineName === undefined) {
@@ -244,9 +278,6 @@ export function registerEngineDetourTool(
244
278
  const definition = createEngineDetourToolDefinition({
245
279
  engineName,
246
280
  ...(engineModel === undefined ? {} : { engineModel }),
247
- fail(error, toolCallId, ctx) {
248
- hostActions.failInfrastructure(error, ctx, toolCallId);
249
- },
250
281
  });
251
282
  roleHost.registerTool(definition);
252
283
 
@@ -154,27 +154,44 @@ export function isEngineDetourFailure(result: {
154
154
  }
155
155
 
156
156
  /**
157
- * Diagnostic string for shared settlement / Terminal Error Artifact (#395).
158
- * Prefer engine stderr 原样; whitespace-only/empty stderr with stdout body must
159
- * carry the child's last result/error row verbatim (e.g. a 529 API Error row) —
160
- * never swallow the cause behind an exit code. Fully-empty output → stable fallback.
157
+ * Seat-visible failure text for the same-session tool result (#1213).
158
+ * Preserve every received stream byte (including whitespace-only) — failure
159
+ * predicates use trim, but content conservation does not. Nonzero exit and
160
+ * empty-stdout reasons also ride this content channel (MCP delivers content,
161
+ * not details). Fully-empty streams → diagnostics only.
161
162
  */
162
- export function engineDetourFailureDiagnostic(result: {
163
+ export function engineDetourFailureSeatText(result: {
163
164
  stderr: string;
164
165
  code: number;
165
166
  stdout: string;
166
167
  }): string {
167
- if (result.stderr.trim().length > 0) return result.stderr;
168
- if (result.stdout.trim() === "") return ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC;
169
- const rows = result.stdout.split("\n");
170
- let lastRow = "";
171
- for (let index = rows.length - 1; index >= 0; index -= 1) {
172
- if (rows[index]!.trim() !== "") {
173
- lastRow = rows[index]!;
174
- break;
168
+ // Length, not trim: whitespace-only streams still reach the seat.
169
+ const parts: string[] = [];
170
+ if (result.stdout.length > 0) parts.push(result.stdout);
171
+ if (result.stderr.length > 0) parts.push(result.stderr);
172
+ let text = "";
173
+ if (parts.length > 0) {
174
+ text = parts[0]!;
175
+ for (let index = 1; index < parts.length; index += 1) {
176
+ if (!text.endsWith("\n")) text += "\n";
177
+ text += parts[index]!;
175
178
  }
176
179
  }
177
- return `劳务引擎以 code ${result.code} 退出:${lastRow}`;
180
+ const append = (line: string): void => {
181
+ if (text.length > 0 && !text.endsWith("\n")) text += "\n";
182
+ text += line;
183
+ };
184
+ // Exit code must reach the seat-visible content channel on every nonzero
185
+ // failure — including when stderr is present (details-only is not enough).
186
+ if (result.code !== 0) {
187
+ append(`劳务引擎以 code ${result.code} 退出`);
188
+ }
189
+ // Empty-stdout failure reason (isEngineDetourFailure) is independent of
190
+ // whether stream bytes were preserved above.
191
+ if (result.stdout.trim() === "") {
192
+ append(ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC);
193
+ }
194
+ return text.length > 0 ? text : ENGINE_DETOUR_EMPTY_STDOUT_DIAGNOSTIC;
178
195
  }
179
196
 
180
197
  /** Non-empty trimmed engine name from process.env, else undefined. */
@@ -9,6 +9,13 @@ type HostSessionEntry = { type: string; message?: HostMessage };
9
9
  export type HostToolResult<T = unknown> = {
10
10
  content: Array<{ type: "text"; text: string } | { type: "image"; data: string; mimeType: string }>;
11
11
  details: T;
12
+ /**
13
+ * Native tool-failure flag (Pi AgentToolResult.isError). Resolved execute
14
+ * results that already identify failure must set this true — describing the
15
+ * failure only in content is not enough. Seat-run death stays a separate
16
+ * infrastructure path (details.cause / kind), not this bit.
17
+ */
18
+ isError?: boolean;
12
19
  terminate?: boolean;
13
20
  };
14
21
 
@@ -483,10 +483,12 @@ export async function prepareRoleEnvelope(options: {
483
483
  }
484
484
  try {
485
485
  const result = await tool.execute(toolCallId, (args ?? {}) as never, undefined, undefined, context);
486
+ // Pass through native isError from the tool result (Pi AgentToolResult).
487
+ // Identified failures must not be projected as tool success (#1213).
486
488
  const projected = await projectToolResult(toolCallId, name, {
487
489
  content: result.content,
488
490
  details: result.details,
489
- isError: false,
491
+ isError: result.isError === true,
490
492
  });
491
493
  if (result.terminate === false) {
492
494
  rejection = {
@@ -1821,7 +1821,7 @@ export function createRoleRuntimeExtension(
1821
1821
  // #357 T2 / #378 / #380 / #391 / #818: any role+engine activation registers the package detour tool once.
1822
1822
  // Gate is resolveEngineName (RoleHost flag → env fallback) — no per-engine execute branch; no role-module spawn.
1823
1823
  if (!engineDetourRegistered) {
1824
- engineDetourRegistered = registerEngineDetourTool(roleHost, hostActions);
1824
+ engineDetourRegistered = registerEngineDetourTool(roleHost);
1825
1825
  }
1826
1826
  // Secretariat owns a declared active surface. The shared engine detour is
1827
1827
  // registered after role activation, so include it here rather than leave