dahrk-node 0.1.22 → 0.1.24

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.
Files changed (2) hide show
  1. package/dist/main.js +60 -9
  2. package/package.json +4 -4
package/dist/main.js CHANGED
@@ -534,19 +534,37 @@ async function runInteractiveLoop(ctx, turns, emit, makeSession, opts) {
534
534
  ...outArtifact ? { artifact: outArtifact } : {}
535
535
  };
536
536
  }
537
+ function classifyRuntimeError(message) {
538
+ const m = message.toLowerCase();
539
+ const transient = m.includes("stream idle timeout") || m.includes("partial response received") || m.includes("overloaded") || /\b529\b/.test(m) || m.includes("rate limit") || m.includes("rate_limit") || m.includes("too many requests") || /\b429\b/.test(m) || m.includes("gateway timeout") || m.includes("bad gateway") || m.includes("service unavailable") || m.includes("internal server error") || /\b50[0234]\b/.test(m) || m.includes("econnreset") || m.includes("connection reset") || m.includes("socket hang up") || m.includes("connection error");
540
+ return transient ? "external" : void 0;
541
+ }
537
542
  async function runBatchLoop(session, ctx, hooks, opts) {
538
543
  let status = "ok";
544
+ let failureClass;
545
+ let summary;
539
546
  try {
540
547
  const tr = await session.sendTurn(resolveStagePrompt(ctx));
541
548
  if (tr.status) status = tr.status;
542
549
  } catch (e) {
543
- if (!opts.cancelled()) hooks.emit({ type: "error", kind: "runtime_error", message: e.message });
550
+ const message = e.message;
551
+ if (!opts.cancelled()) {
552
+ hooks.emit({ type: "error", kind: "runtime_error", message });
553
+ failureClass = classifyRuntimeError(message);
554
+ if (failureClass) summary = `upstream API transient: ${message}`;
555
+ }
544
556
  status = "fail";
545
557
  }
546
558
  if (opts.cancelled()) status = "fail";
547
559
  const costUsd = session.cost();
548
560
  const sessionId = session.sessionId;
549
- return { status, ...sessionId ? { sessionId } : {}, ...costUsd !== void 0 ? { costUsd } : {} };
561
+ return {
562
+ status,
563
+ ...summary !== void 0 ? { summary } : {},
564
+ ...failureClass ? { failureClass } : {},
565
+ ...sessionId ? { sessionId } : {},
566
+ ...costUsd !== void 0 ? { costUsd } : {}
567
+ };
550
568
  }
551
569
 
552
570
  // ../../packages/executor-worktree/src/stage-complete-tool.ts
@@ -1334,8 +1352,11 @@ async function defaultCreatePiSession(ctx) {
1334
1352
  if (ctx.config.model) {
1335
1353
  const resolved = resolveCliModel({ cliModel: ctx.config.model, modelRegistry });
1336
1354
  if (!resolved?.error) {
1337
- model = pickAuthedModel(resolved?.model, modelRegistry.getAvailable());
1355
+ model = pickAuthedModel(resolved?.model, modelRegistry.getAvailable(), hint2?.defaultModel);
1338
1356
  }
1357
+ } else if (hint2?.defaultModel) {
1358
+ const resolved = resolveCliModel({ cliModel: hint2.defaultModel, modelRegistry });
1359
+ if (!resolved?.error) model = pickAuthedModel(resolved?.model, modelRegistry.getAvailable());
1339
1360
  }
1340
1361
  const stageComplete = defineTool({
1341
1362
  name: PI_STAGE_COMPLETE_TOOL,
@@ -1431,12 +1452,20 @@ function modelFamily(id) {
1431
1452
  const last = id.split(".").pop() ?? id;
1432
1453
  return last.replace(/-v\d+:\d+$/, "").toLowerCase();
1433
1454
  }
1434
- function pickAuthedModel(resolved, available) {
1455
+ function pickAuthedModel(resolved, available, fallbackModel) {
1435
1456
  if (!resolved || !available?.length) return resolved;
1436
1457
  const providers = new Set(available.map((m) => m.provider));
1437
1458
  if (providers.has(resolved.provider)) return resolved;
1438
1459
  const family = modelFamily(resolved.id);
1439
- return available.find((m) => modelFamily(m.id) === family) ?? resolved;
1460
+ const sameFamily = available.find((m) => modelFamily(m.id) === family);
1461
+ if (sameFamily) return sameFamily;
1462
+ if (fallbackModel) {
1463
+ const byId = available.find((m) => m.id === fallbackModel);
1464
+ if (byId) return byId;
1465
+ const byFamily = available.find((m) => modelFamily(m.id) === modelFamily(fallbackModel));
1466
+ if (byFamily) return byFamily;
1467
+ }
1468
+ return resolved;
1440
1469
  }
1441
1470
 
1442
1471
  // ../../packages/executor-worktree/src/pi-container.ts
@@ -1916,6 +1945,17 @@ function createGitService(opts = {}) {
1916
1945
  }
1917
1946
  git(mirror, ["config", "--replace-all", "remote.origin.fetch", TRACKING_REFSPEC]);
1918
1947
  };
1948
+ const reconcileMirrorUrl = (mirror, repoId, gitUrl, authEnv) => {
1949
+ const expected = authEnv ? withTokenUser(gitUrl) : gitUrl;
1950
+ let current = "";
1951
+ try {
1952
+ current = git(mirror, ["config", "--get", "remote.origin.url"]).trim();
1953
+ } catch {
1954
+ }
1955
+ if (current === expected) return;
1956
+ log.info(`mirror ${repoId}: origin url drifted (${current} -> ${expected}); updating in place`);
1957
+ git(mirror, ["remote", "set-url", "origin", expected]);
1958
+ };
1919
1959
  const salvageOrphanedTip = (mirror, branchName, start2) => {
1920
1960
  try {
1921
1961
  if (!gitOk2(mirror, ["rev-parse", "--verify", "-q", `refs/heads/${branchName}`])) return;
@@ -1956,6 +1996,7 @@ function createGitService(opts = {}) {
1956
1996
  log.info(`refreshing mirror ${repoId}`);
1957
1997
  try {
1958
1998
  migrateMirrorConfig(mirror, repoId);
1999
+ reconcileMirrorUrl(mirror, repoId, gitUrl, authEnv);
1959
2000
  git(mirror, ["fetch", "--prune", "origin"], netEnv(authEnv));
1960
2001
  gcShadowHeads(mirror);
1961
2002
  return { mirror, refreshed: true };
@@ -4084,7 +4125,7 @@ function createStageRunner(deps) {
4084
4125
  }
4085
4126
  sink.finalised({ ...base, meta: finalMeta, eventCount: writer.count(), ...archiveKey ? { archiveKey } : {} });
4086
4127
  };
4087
- const finish = async (status2, summary2, sessionId, costUsd, handedBackDoc) => {
4128
+ const finish = async (status2, summary2, sessionId, costUsd, handedBackDoc, failureClass2) => {
4088
4129
  active.delete(jobId);
4089
4130
  turnQueues.delete(jobId);
4090
4131
  await gateway?.stop().catch((e) => log.warn({ err: e, jobId }, "mcp gateway: stop failed"));
@@ -4121,7 +4162,8 @@ function createStageRunner(deps) {
4121
4162
  summary: summary2,
4122
4163
  ...sessionId ? { sessionId } : {},
4123
4164
  ...costUsd !== void 0 ? { costUsd } : {},
4124
- ...resolved ? { artifact: resolved.artifact } : {}
4165
+ ...resolved ? { artifact: resolved.artifact } : {},
4166
+ ...failureClass2 ? { failureClass: failureClass2 } : {}
4125
4167
  };
4126
4168
  };
4127
4169
  if (deps.packCache && job.provision && job.provision.length > 0) {
@@ -4245,6 +4287,14 @@ function createStageRunner(deps) {
4245
4287
  // never surfaced to the agent's own tool calls. Absent on ambient nodes; inert for the Claude/
4246
4288
  // Codex adapters, which use ambient inference.
4247
4289
  ...job.runtimeEnv ? { runtimeEnv: job.runtimeEnv } : {},
4290
+ // The brokered auth-profile hint (DHK-509/511): WHICH provider each piece of the inference
4291
+ // auth above belongs to, plus the model fallback. The adapter applies nothing for a provider
4292
+ // the hint does not name, so without this passthrough `runtimeEnv` arrives and is ignored -
4293
+ // a managed node then has no inference auth at all and falls through to whatever provider the
4294
+ // runtime defaults to. Read through a narrow cast because the field is not in the published
4295
+ // `@dahrk/contracts` (^0.4.0) yet; this line and `readAuthHint` are the only two seams, and
4296
+ // both drop the cast when contracts ships.
4297
+ ...job.runtimeAuth ? { runtimeAuth: job.runtimeAuth } : {},
4248
4298
  // The adapter persists each runtime-native record under the attempt's raw/ sidecar
4249
4299
  // and stamps the rawRef onto the emitted event.
4250
4300
  writeRaw: writer.writeRaw,
@@ -4323,7 +4373,8 @@ function createStageRunner(deps) {
4323
4373
  }
4324
4374
  }
4325
4375
  if (denied) summary += "\n\n(note: one or more tool actions were blocked by a deny-only policy guard.)";
4326
- return finish(status, summary, result.sessionId ?? job.sessionId, result.costUsd, result.artifact);
4376
+ const failureClass = timedOut || stalled ? "harness" : result.failureClass;
4377
+ return finish(status, summary, result.sessionId ?? job.sessionId, result.costUsd, result.artifact, failureClass);
4327
4378
  } finally {
4328
4379
  inFlight.set(runId, Math.max(0, (inFlight.get(runId) ?? 1) - 1));
4329
4380
  }
@@ -7388,7 +7439,7 @@ async function runStatus(inputs, deps) {
7388
7439
  }
7389
7440
 
7390
7441
  // src/main.ts
7391
- var CLIENT_VERSION = "0.1.22";
7442
+ var CLIENT_VERSION = "0.1.24";
7392
7443
  var DEFAULT_HUB_URL = "wss://api.dahrk.ai";
7393
7444
  var list = (v) => (v ?? "").split(",").map((s) => s.trim()).filter(Boolean);
7394
7445
  var RUNTIMES2 = ["claude-code", "codex", "pi"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dahrk-node",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "private": false,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -34,7 +34,7 @@
34
34
  "exports": "./dist/main.js",
35
35
  "dependencies": {
36
36
  "@anthropic-ai/claude-agent-sdk": "0.3.183",
37
- "@dahrk/contracts": "^0.4.0",
37
+ "@dahrk/contracts": "^0.6.0",
38
38
  "pino": "^10.3.1",
39
39
  "ws": "^8.18.0",
40
40
  "zod": "^3.23.8"
@@ -42,8 +42,8 @@
42
42
  "devDependencies": {
43
43
  "tsup": "^8.3.5",
44
44
  "tsx": "^4.19.0",
45
- "@dahrk/edge": "0.1.0",
46
- "@dahrk/executor-worktree": "0.1.0"
45
+ "@dahrk/executor-worktree": "0.1.0",
46
+ "@dahrk/edge": "0.1.0"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "tsup",