ennodia 0.5.0 → 0.5.1

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.
@@ -732,6 +732,8 @@ After the worker settles, inspect its task receipt. If `canContinue` is true,
732
732
  pass that task ID as `continueTaskId` in the next run. The follow-up keeps the
733
733
  harness and directory. It inherits the model and reasoning effort unless you
734
734
  supply new values. Each follow-up has a new task ID and execution allowance.
735
+
736
+ Readiness checks use the inherited or explicitly supplied model before the follow-up starts.
735
737
  An earlier turn cannot be used once a follow-up has started.
736
738
 
737
739
  Continuation requires a task still held by the same running Ennodia server.
@@ -147,7 +147,9 @@ Replace the key through the native credential flow for the intended provider.
147
147
  Signing in again does not renew an expired API key.
148
148
  `quota-exhausted` includes depleted credits.
149
149
  Restore credits or quota through the intended provider before rechecking.
150
+
150
151
  Ennodia reads terminal-formatted native errors and returns the matching recovery action.
152
+ A worker's plain-text answer does not trigger a sign-in or capacity recovery action.
151
153
 
152
154
  Hermes uses the full `hermes chat` CLI. The legacy `hermes-agent` executable is not compatible.
153
155
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ennodia",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "mcpName": "io.github.cherninlab/ennodia",
5
5
  "description": "Connects the AI agents you use: your agent hands a task to another and gets the answer back",
6
6
  "license": "MIT",
package/src/core.ts CHANGED
@@ -882,9 +882,9 @@ export class EnnodiaCore {
882
882
  }
883
883
 
884
884
  async startRun(input: RunStartWithSkillIdsInput): Promise<RunView> {
885
- if (input.continueTaskId && !input.cwd) {
885
+ if (input.continueTaskId) {
886
886
  const previous = this.taskManager.get(input.continueTaskId, { includeOutput: false, includeEvents: false });
887
- if (previous) input = { ...input, cwd: previous.cwd };
887
+ if (previous) input = { ...input, cwd: input.cwd ?? previous.cwd, model: input.model ?? previous.model };
888
888
  }
889
889
  const { skillIds, ...runInput } = input;
890
890
  const skills = runInput.skills ??
package/src/readiness.ts CHANGED
@@ -41,16 +41,20 @@ export class HarnessReadinessError extends Error {
41
41
  }
42
42
 
43
43
  /** Recognize native failure envelopes, never quoted prose inside an answer. */
44
- export function nativeReadinessFailure(stdout: string, stderr: string): ReadinessState | undefined {
45
- const lines = [...stripVTControlCharacters(stderr).split(/\r?\n/), ...stripVTControlCharacters(stdout).split(/\r?\n/)];
46
- for (const line of lines) {
44
+ export function nativeReadinessFailure(stdout: string, stderr: string, allowPlainStdout = false): ReadinessState | undefined {
45
+ const stderrLines = stripVTControlCharacters(stderr).split(/\r?\n/);
46
+ const lines = [...stderrLines, ...stripVTControlCharacters(stdout).split(/\r?\n/)];
47
+ for (const [index, line] of lines.entries()) {
47
48
  let message = line.trim();
48
49
  try {
49
50
  const event = JSON.parse(line);
50
51
  if (event.type === "error") message = typeof event.message === "string" ? event.message : event.error?.message ?? "";
51
52
  else if (event.type === "turn.failed") message = event.error?.message ?? "";
52
53
  else continue;
53
- } catch { /* native text CLIs have no JSON envelope */ }
54
+ } catch {
55
+ // Only status/nonce checks can distinguish plain stdout from worker prose.
56
+ if (index >= stderrLines.length && !allowPlainStdout) continue;
57
+ }
54
58
  message = stripVTControlCharacters(message).trim();
55
59
  if (/^(?:Error:\s*)?(?:This |The |Your )?API key (?:has expired|is expired)\b/i.test(message)) return "credentials-expired";
56
60
  if (/^(?:(?:API )?Error:\s*)?(?:Failed to authenticate\b|OAuth session expired\b|Authentication failed\b|Not logged in\b|Unauthorized\b|401\b|Re-authentication required\b|No access token found\b|Token refresh failed\b|Failed to refresh (?:OAuth|token)\b|Your authentication token .*expired\b)/i.test(message)) return "login-required";
@@ -192,7 +196,7 @@ export class HarnessReadinessManager {
192
196
  const auth = recipe(h.id).auth;
193
197
  if (auth) {
194
198
  const result = await this.runner({ command: h.commandPath, args: auth }, 5_000);
195
- const state = nativeReadinessFailure(result.stdout, result.stderr);
199
+ const state = nativeReadinessFailure(result.stdout, result.stderr, true);
196
200
  if (state) return this.value(h, state, input.model);
197
201
  if (h.id === "claude-code") {
198
202
  try { if (JSON.parse(result.stdout).loggedIn === false) return this.value(h, "login-required", input.model); } catch { /* verification below */ }
@@ -206,7 +210,7 @@ export class HarnessReadinessManager {
206
210
  try {
207
211
  const spec = adapter.buildCommand(h.commandPath, { prompt: `Reply with exactly ${nonce}. Do not use tools, read files, or perform any other work.`, cwd: dir, model: input.model, timeoutMs: 20_000, finalMessagePath: join(dir, "answer.txt"), nativeSandbox: adapter.supportsNativeSandbox ? "read-only" : undefined });
208
212
  const result = await this.runner(spec, 25_000);
209
- const failure = nativeReadinessFailure(result.stdout, result.stderr);
213
+ const failure = nativeReadinessFailure(result.stdout, result.stderr, true);
210
214
  if (failure) return this.value(h, failure, input.model);
211
215
  if (result.timedOut || result.exitCode !== 0 || adapter.failureReason?.(result.stdout, result.stderr)) return this.value(h, "unreachable", input.model);
212
216
  let answer = adapter.readinessAnswer ? adapter.readinessAnswer(result.stdout) ?? "" : result.stdout;
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const ENNODIA_VERSION = "0.5.0";
1
+ export const ENNODIA_VERSION = "0.5.1";