c8ctl-plugin-nano 1.63.2 → 1.64.0
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.
- package/agent-instance.mjs +61 -1
- package/c8ctl-plugin.js +184 -2
- package/package.json +8 -8
package/agent-instance.mjs
CHANGED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
// append an AgentInstance must NEVER crash the harness or change `job.complete`
|
|
20
20
|
// behaviour — the AgentInstance lifecycle is orthogonal to job completion.
|
|
21
21
|
|
|
22
|
+
import { hostname } from 'node:os';
|
|
23
|
+
|
|
22
24
|
import { sessionAcp as defaultSessionAcp } from './agentic.mjs';
|
|
23
25
|
|
|
24
26
|
// The two AgentInstance surfaces we call on the host SDK client. A client missing
|
|
@@ -296,6 +298,49 @@ export function deriveAgentDefinition({ profile, envelope } = {}) {
|
|
|
296
298
|
return { model, provider, systemPrompt };
|
|
297
299
|
}
|
|
298
300
|
|
|
301
|
+
// #243: marker discriminating the provenance blob inside a CONFIGURATION turn's
|
|
302
|
+
// `content[]` from an ordinary message/tool OBJECT block. Versioned so a consumer can
|
|
303
|
+
// evolve the shape without ambiguity.
|
|
304
|
+
export const PROVENANCE_KIND = 'nanobpm.provenance/v1';
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* #243: build the parity-safe provenance content block for the opening CONFIGURATION
|
|
308
|
+
* turn.
|
|
309
|
+
*
|
|
310
|
+
* Camunda's AgentInstance schema pins the CONFIGURATION `definition` to
|
|
311
|
+
* model/provider/systemPrompt (no metadata/attributes field), and nanobpmn promises
|
|
312
|
+
* parity — so producer-invented top-level fields are off-limits. BUT a history item's
|
|
313
|
+
* `content[]` is a discriminated union that already includes an `OBJECT` variant
|
|
314
|
+
* (`{ contentType: 'OBJECT', object: <arbitrary JSON> }`) — the SAME shape the producer
|
|
315
|
+
* already emits for a structured tool result (`contentForResult`). Ride it to attribute
|
|
316
|
+
* a run to the agent harness (`agentName`), the nano runtime (`runtimeVersion` — the
|
|
317
|
+
* plugin `package.json` version, which is one and the same as the nano supervisor
|
|
318
|
+
* version), and, best-effort, the underlying agent CLI (`agentCliVersion`), WITHOUT
|
|
319
|
+
* diverging from the Camunda API.
|
|
320
|
+
*
|
|
321
|
+
* Returns `null` when no substantive identity field is present, so a bare `host`/`pid`
|
|
322
|
+
* (diagnostics only) never emits a noisy content block.
|
|
323
|
+
*
|
|
324
|
+
* @param {object} [p]
|
|
325
|
+
* @param {object} [p.profile] Worker profile (`name` → `agentName`).
|
|
326
|
+
* @param {string} [p.runtimeVersion] Nano plugin/supervisor version (`pluginVersion`).
|
|
327
|
+
* @param {string} [p.agentCliVersion] Best-effort underlying-CLI version (may be blank).
|
|
328
|
+
* @param {string} [p.host] Host name (diagnostic).
|
|
329
|
+
* @param {number} [p.pid] Worker pid (diagnostic).
|
|
330
|
+
* @returns {{ contentType: 'OBJECT', object: object } | null}
|
|
331
|
+
*/
|
|
332
|
+
export function buildProvenanceContent({ profile, runtimeVersion, agentCliVersion, host, pid } = {}) {
|
|
333
|
+
const object = { kind: PROVENANCE_KIND };
|
|
334
|
+
if (isNonBlank(profile?.name)) object.agentName = String(profile.name);
|
|
335
|
+
if (isNonBlank(runtimeVersion)) object.runtimeVersion = String(runtimeVersion);
|
|
336
|
+
if (isNonBlank(agentCliVersion)) object.agentCliVersion = String(agentCliVersion);
|
|
337
|
+
if (isNonBlank(host)) object.host = String(host);
|
|
338
|
+
if (Number.isInteger(pid) && pid > 0) object.pid = pid;
|
|
339
|
+
// Only agent/runtime identity justifies a block; host/pid alone are diagnostics.
|
|
340
|
+
if (!(object.agentName || object.runtimeVersion || object.agentCliVersion)) return null;
|
|
341
|
+
return { contentType: 'OBJECT', object };
|
|
342
|
+
}
|
|
343
|
+
|
|
299
344
|
// Map the ACP classifier's message role to the AgentHistory role enum. ACP has no
|
|
300
345
|
// distinct REASONING role, so a `reasoning` chunk folds into ASSISTANT.
|
|
301
346
|
function historyRole(acpRole) {
|
|
@@ -396,6 +441,15 @@ export function createAgentInstanceProducer(opts = {}) {
|
|
|
396
441
|
logger = console,
|
|
397
442
|
now = () => Date.now(),
|
|
398
443
|
sessionAcp = defaultSessionAcp,
|
|
444
|
+
// #243: durable-transcript provenance for the opening CONFIGURATION turn. The nano
|
|
445
|
+
// runtime version (== the supervisor version) and the agent harness name/CLI version
|
|
446
|
+
// ride a parity-safe OBJECT content block (see `buildProvenanceContent`). All are
|
|
447
|
+
// best-effort/optional — a blank value simply omits its field, and if none are
|
|
448
|
+
// present no provenance block is emitted. `host`/`pid` default to this process.
|
|
449
|
+
runtimeVersion = '',
|
|
450
|
+
agentCliVersion = '',
|
|
451
|
+
host = hostname(),
|
|
452
|
+
pid = process.pid,
|
|
399
453
|
createRetryBaseMs = DEFAULT_CREATE_RETRY_BASE_MS,
|
|
400
454
|
createRetryMaxMs = DEFAULT_CREATE_RETRY_MAX_MS,
|
|
401
455
|
preMintBufferMax = DEFAULT_PRE_MINT_BUFFER_MAX,
|
|
@@ -743,11 +797,17 @@ export function createAgentInstanceProducer(opts = {}) {
|
|
|
743
797
|
// Build the opening CONFIGURATION turn from the concrete runtime definition.
|
|
744
798
|
const buildConfigTurn = () => {
|
|
745
799
|
const def = deriveAgentDefinition({ profile, envelope });
|
|
800
|
+
// #243: a parity-safe provenance OBJECT (agent name, nano runtime/supervisor
|
|
801
|
+
// version, best-effort agent-CLI version) rides the CONFIGURATION turn's content[]
|
|
802
|
+
// — the same OBJECT content variant the producer already emits for tool results —
|
|
803
|
+
// so no field is invented on the Camunda-pinned CONFIGURATION definition. Omitted
|
|
804
|
+
// entirely when no substantive identity is available.
|
|
805
|
+
const provenance = buildProvenanceContent({ profile, runtimeVersion, agentCliVersion, host, pid });
|
|
746
806
|
const configTurn = {
|
|
747
807
|
historyItemId: `configuration:${elementInstanceKey}`,
|
|
748
808
|
loopIteration: 1,
|
|
749
809
|
role: 'CONFIGURATION',
|
|
750
|
-
content: [],
|
|
810
|
+
content: provenance ? [provenance] : [],
|
|
751
811
|
producedAt: iso(),
|
|
752
812
|
model: def.model,
|
|
753
813
|
provider: def.provider,
|
package/c8ctl-plugin.js
CHANGED
|
@@ -58,7 +58,7 @@ import { lookup as dnsLookup } from 'node:dns/promises';
|
|
|
58
58
|
import * as nodeDns from 'node:dns';
|
|
59
59
|
import { randomUUID, createHash, randomBytes } from 'node:crypto';
|
|
60
60
|
import { homedir, platform as osPlatform, devNull, tmpdir, hostname } from 'node:os';
|
|
61
|
-
import { join, isAbsolute, resolve as resolvePath, dirname, basename, sep } from 'node:path';
|
|
61
|
+
import { join, isAbsolute, resolve as resolvePath, dirname, basename, sep, delimiter } from 'node:path';
|
|
62
62
|
import { createRequire } from 'node:module';
|
|
63
63
|
import { fileURLToPath } from 'node:url';
|
|
64
64
|
import { createInterface } from 'node:readline/promises';
|
|
@@ -1721,6 +1721,104 @@ function buildAgentCommandLine(command, args) {
|
|
|
1721
1721
|
return `${command} ${list.map(shQuote).join(' ')}`;
|
|
1722
1722
|
}
|
|
1723
1723
|
|
|
1724
|
+
// #243: extract a plausible version token from an agent CLI's `--version` output for
|
|
1725
|
+
// the durable transcript's provenance block. Prefers a semver-ish token, else the
|
|
1726
|
+
// first non-empty line; length-capped so a chatty (or adversarial) harness can't bloat
|
|
1727
|
+
// the transcript. Returns null when nothing usable is present.
|
|
1728
|
+
export function extractVersionToken(text) {
|
|
1729
|
+
const s = String(text || '');
|
|
1730
|
+
const semver = s.match(/\bv?\d+\.\d+(?:\.\d+)?(?:[-+][0-9A-Za-z.-]+)?\b/);
|
|
1731
|
+
if (semver) return semver[0].slice(0, 64);
|
|
1732
|
+
const firstLine = s.split(/\r?\n/).map((l) => l.trim()).find((l) => l.length > 0);
|
|
1733
|
+
return firstLine ? firstLine.slice(0, 64) : null;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// #243: best-effort probe of an agent harness CLI's OWN version (distinct from the nano
|
|
1737
|
+
// plugin/supervisor version), for the durable transcript's provenance block. Runs
|
|
1738
|
+
// `<command> --version` through a shell (PATH resolution, mirroring how the harness is
|
|
1739
|
+
// spawned) with a hard timeout + SIGKILL and stdin closed so an interactive harness
|
|
1740
|
+
// gets EOF and exits rather than hanging. Restricted to a BARE single executable and run
|
|
1741
|
+
// under the caller-supplied `env` (the merged profile env) so it can neither run an
|
|
1742
|
+
// embedded-argument command nor resolve a different PATH binary than the real run
|
|
1743
|
+
// (#257). ENTIRELY best-effort: any failure, timeout, non-zero exit with no version-ish
|
|
1744
|
+
// output, an embedded/compound command, or unrecognizable output yields null (the field
|
|
1745
|
+
// is simply omitted). Meant to be called ONCE per worker process (the command is fixed
|
|
1746
|
+
// per profile), so it never taxes the activation hot path. Disable via
|
|
1747
|
+
// NANO_AGENT_CLI_PROBE=off. The spawner is injected for deterministic testing.
|
|
1748
|
+
function probeAgentCliVersion(command, { timeoutMs = 1500, run = spawnSync, env = undefined } = {}) {
|
|
1749
|
+
if (typeof command !== 'string' || command.trim() === '') return null;
|
|
1750
|
+
if (String(process.env.NANO_AGENT_CLI_PROBE || '').trim().toLowerCase() === 'off') return null;
|
|
1751
|
+
// #257 review: only probe a BARE single executable. `buildAgentCommandLine`
|
|
1752
|
+
// preserves an embedded-argument `command` verbatim when structured args are
|
|
1753
|
+
// empty (e.g. `command: "node agent.js"`), so `${command} --version` would run
|
|
1754
|
+
// that script/compound command — probing the interpreter or a wholly different
|
|
1755
|
+
// program — and persist a false `agentCliVersion`. A token carrying whitespace or
|
|
1756
|
+
// shell metacharacters is not a bare executable: omit the probe rather than
|
|
1757
|
+
// record a wrong (or side-effecting) reading. A plain path token (with / \ : . _ -)
|
|
1758
|
+
// is still allowed so an absolute harness path probes normally.
|
|
1759
|
+
if (!/^[\w./\\:+-]+$/.test(command.trim())) return null;
|
|
1760
|
+
// #257 review: SKIP a RELATIVE path command (a token carrying a path separator
|
|
1761
|
+
// that is not absolute, e.g. `./harness`, `../bin/tool`, `sub/dir/cmd`). Unlike a
|
|
1762
|
+
// bare PATH-resolved name (cwd-independent) or an absolute path (fully determined),
|
|
1763
|
+
// a relative path resolves against the probe's cwd — which is the WORKER's cwd, NOT
|
|
1764
|
+
// the per-job `cwd` (a fresh run dir / cloned repo) the harness is actually launched
|
|
1765
|
+
// from in `runAgentJob`. Probing `./harness` here could read a different file/version
|
|
1766
|
+
// than the command the job runs (or find nothing where the job would), persisting a
|
|
1767
|
+
// false/omitted `agentCliVersion`. Best-effort: omit rather than record a wrong
|
|
1768
|
+
// reading for a command whose resolution is cwd-ambiguous.
|
|
1769
|
+
{
|
|
1770
|
+
const cmd = command.trim();
|
|
1771
|
+
if (/[\\/]/.test(cmd) && !isAbsolute(cmd)) return null;
|
|
1772
|
+
// #257 review: a BARE PATH-resolved name is only cwd-independent if PATH itself
|
|
1773
|
+
// is. If the caller-supplied env's PATH carries a RELATIVE or EMPTY entry (e.g.
|
|
1774
|
+
// `.`, an empty field meaning cwd, or `./node_modules/.bin`), resolution depends
|
|
1775
|
+
// on the working directory — and the probe's cwd (the WORKER's) is NOT the per-job
|
|
1776
|
+
// cwd (the run dir / cloned repo) the harness is launched from — so `--version`
|
|
1777
|
+
// could resolve a different executable/version than the job's. Omit rather than
|
|
1778
|
+
// record a cwd-ambiguous reading. An ABSOLUTE command bypasses PATH entirely, so
|
|
1779
|
+
// it is unaffected; a probe with no explicit `env` inherits `process.env` verbatim
|
|
1780
|
+
// (the caller opted into that resolution) and is left untouched.
|
|
1781
|
+
if (env && !isAbsolute(cmd)) {
|
|
1782
|
+
const pathVar = env.PATH ?? env.Path ?? env.path ?? '';
|
|
1783
|
+
if (String(pathVar).split(delimiter).some((e) => e === '' || !isAbsolute(e))) return null;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
let out;
|
|
1787
|
+
try {
|
|
1788
|
+
out = run(`${command} --version`, {
|
|
1789
|
+
shell: true,
|
|
1790
|
+
timeout: timeoutMs,
|
|
1791
|
+
killSignal: 'SIGKILL',
|
|
1792
|
+
encoding: 'utf8',
|
|
1793
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1794
|
+
// #257 review: run the probe under the SAME environment the harness will run
|
|
1795
|
+
// under (the profile env merged over the host env), so PATH resolution matches
|
|
1796
|
+
// the real invocation and the probe can't resolve a different binary than the
|
|
1797
|
+
// one the job will spawn. Undefined inherits `process.env` (spawnSync default).
|
|
1798
|
+
...(env ? { env } : {}),
|
|
1799
|
+
// #257 review: cap the captured output. Without a finite maxBuffer a harness
|
|
1800
|
+
// that streams continuously during the timeout window makes each worker retain
|
|
1801
|
+
// unbounded stdout/stderr (exhausting memory) before the 64-byte transcript cap
|
|
1802
|
+
// is ever applied. On overflow spawnSync sets `out.error` (ENOBUFS), which the
|
|
1803
|
+
// guard below already rejects — so the probe stays best-effort (yields null).
|
|
1804
|
+
maxBuffer: 256 * 1024,
|
|
1805
|
+
windowsHide: true,
|
|
1806
|
+
});
|
|
1807
|
+
} catch {
|
|
1808
|
+
return null;
|
|
1809
|
+
}
|
|
1810
|
+
if (!out) return null;
|
|
1811
|
+
// #257 review: spawnSync does NOT throw for a normal shell failure or timeout —
|
|
1812
|
+
// with `shell: true` it returns a result carrying `error` (spawn failure /
|
|
1813
|
+
// ETIMEDOUT) and/or a non-zero/null exit `status`, alongside diagnostic text on
|
|
1814
|
+
// stderr (e.g. `/bin/sh: <cmd>: not found`) or a partial capture. Feeding that to
|
|
1815
|
+
// extractVersionToken (whose fallback accepts the first non-empty line) would
|
|
1816
|
+
// persist the error banner as a bogus `agentCliVersion`. Trust only a clean exit
|
|
1817
|
+
// (no `error`, status 0); anything else omits the field.
|
|
1818
|
+
if (out.error || out.status !== 0) return null;
|
|
1819
|
+
return extractVersionToken(`${out.stdout || ''}\n${out.stderr || ''}`);
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1724
1822
|
// A worker job-type token: rank/capability tokens use `:` (rank↔cap) and `+`
|
|
1725
1823
|
// (combined caps) as delimiters, and code-first `@nanobpm/workflow` job types
|
|
1726
1824
|
// are `<flowId>:<taskName>` or an explicit override. The first character must be
|
|
@@ -8753,6 +8851,17 @@ async function workAgent(req, flags, ctx) {
|
|
|
8753
8851
|
const workerPidStart = pidStartToken(process.pid);
|
|
8754
8852
|
let pluginVersion = null;
|
|
8755
8853
|
try { pluginVersion = JSON.parse(readFileSync(join(pluginDir, 'package.json'), 'utf-8')).version ?? null; } catch { /* best effort */ }
|
|
8854
|
+
// #243: the agent harness CLI's own version is probed ONCE per worker process
|
|
8855
|
+
// (bounded, best-effort) so the durable transcript's provenance block can attribute
|
|
8856
|
+
// a run to a specific harness build. The probe is DEFERRED until the FIRST
|
|
8857
|
+
// external-agent activation (its only consumer is the AgentInstance producer), so a
|
|
8858
|
+
// worker that only services ordinary jobs never runs the harness `--version`. The
|
|
8859
|
+
// probe env is WORKER-STATIC — `process.env` merged with the profile `env` only,
|
|
8860
|
+
// NOT the per-job `setup.env` — so the once-per-worker cache can't leak one job's
|
|
8861
|
+
// setup-derived reading to later jobs (#257). Never fatal — a null result just omits
|
|
8862
|
+
// the field. `agentCliProbed` latches the once-only semantics.
|
|
8863
|
+
let agentCliVersion = null;
|
|
8864
|
+
let agentCliProbed = false;
|
|
8756
8865
|
let workerNsDir;
|
|
8757
8866
|
try {
|
|
8758
8867
|
({ nsDir: workerNsDir } = allocateWorkerNamespace({
|
|
@@ -8864,6 +8973,66 @@ async function workAgent(req, flags, ctx) {
|
|
|
8864
8973
|
// active profile itself — identical to the old no-arg behaviour.
|
|
8865
8974
|
const camunda = globalThis.c8ctl.createClient(resolveConnectionProfile(ctx));
|
|
8866
8975
|
|
|
8976
|
+
// #243/#257: precompute the job-independent gates for the harness `--version`
|
|
8977
|
+
// probe (the probe itself is DEFERRED to the first external-agent activation
|
|
8978
|
+
// below — see maybeProbeAgentCliVersion). The probe only yields a consumable
|
|
8979
|
+
// reading when:
|
|
8980
|
+
// - HOST execution (a container job runs `sh -c` inside the selected image, so a
|
|
8981
|
+
// same-named host binary would report a plausible-but-wrong version);
|
|
8982
|
+
// - the AgentInstance kill-switch is not set (NANO_AGENT_INSTANCE=off disables the
|
|
8983
|
+
// producer — the probe's only consumer — so probing would needlessly run a
|
|
8984
|
+
// possibly non-idempotent harness with no reader);
|
|
8985
|
+
// - the host SDK actually supports AgentInstance (create/update). The producer
|
|
8986
|
+
// degrades to a disabled facade for older clients, so probing under such a client
|
|
8987
|
+
// is pure waste + an unnecessary side effect (Copilot review, #257);
|
|
8988
|
+
// - the ACP classifier is available. `createAgentInstanceProducer` is ALSO inert
|
|
8989
|
+
// (the `usable` gate requires `classifyUpdate`) when the agentic classifier is
|
|
8990
|
+
// missing, even with a create/update-capable SDK — so probing would run the
|
|
8991
|
+
// harness `--version` for a producer that can never mint a transcript to consume
|
|
8992
|
+
// it. Mirror that precondition here so the probe stays side-effect-free whenever
|
|
8993
|
+
// the producer is disabled (Copilot review, #257);
|
|
8994
|
+
// - the profile command alone is the full invocation (no extra args). An
|
|
8995
|
+
// interpreter-style profile (`command:'node', args:['agent.js']`) would probe the
|
|
8996
|
+
// interpreter, not the harness — omit rather than misattribute.
|
|
8997
|
+
// The probe itself (probeAgentCliVersion) additionally refuses an embedded-argument
|
|
8998
|
+
// command and runs under the WORKER-STATIC profile env (see maybeProbeAgentCliVersion
|
|
8999
|
+
// for why per-job setup.env is deliberately excluded), so PATH resolves a stable
|
|
9000
|
+
// representative harness binary.
|
|
9001
|
+
const agentInstanceProbeOff = String(process.env.NANO_AGENT_INSTANCE || '').trim().toLowerCase() === 'off';
|
|
9002
|
+
const sdkSupportsAgentInstance =
|
|
9003
|
+
!!camunda &&
|
|
9004
|
+
typeof camunda.createAgentInstance === 'function' &&
|
|
9005
|
+
typeof camunda.updateAgentInstance === 'function';
|
|
9006
|
+
// The producer's `usable` gate also requires the ACP classifier (agent-instance.mjs);
|
|
9007
|
+
// without it the producer is inert regardless of SDK support, so exclude the probe too.
|
|
9008
|
+
const acpClassifierAvailable = typeof agenticSessionAcp?.classifyUpdate === 'function';
|
|
9009
|
+
const agentCliProbeEligible =
|
|
9010
|
+
!isContainer && !agentInstanceProbeOff && sdkSupportsAgentInstance && acpClassifierAvailable && effectiveArgs.length === 0;
|
|
9011
|
+
// #257 review: DEFER the probe until an external-agent job is actually being
|
|
9012
|
+
// serviced — so a worker that only ever receives ordinary service jobs never runs
|
|
9013
|
+
// the harness `--version` at all (no wasted startup delay / side effect). Runs at
|
|
9014
|
+
// most once per worker (cached), so the per-job hot path pays nothing after the
|
|
9015
|
+
// first external activation. The probe env is WORKER-STATIC — `process.env` merged
|
|
9016
|
+
// with the profile `env` only, NOT the per-job `setup.env`: `agentCliProbed` is a
|
|
9017
|
+
// worker-wide latch, so folding a single job's `setup.env` (which can change PATH,
|
|
9018
|
+
// and thus which CLI build resolves) into the cache would leak THAT job's reading
|
|
9019
|
+
// to every later job serviced by the same worker (#257). A worker-static basis makes
|
|
9020
|
+
// the cached `agentCliVersion` a correct-by-construction representative reading for
|
|
9021
|
+
// the profile; a job whose `setup.env` genuinely alters the resolved binary is a
|
|
9022
|
+
// per-job divergence the single cached provenance reading deliberately does not chase.
|
|
9023
|
+
const maybeProbeAgentCliVersion = () => {
|
|
9024
|
+
if (agentCliProbed || !agentCliProbeEligible) return;
|
|
9025
|
+
agentCliProbed = true;
|
|
9026
|
+
try {
|
|
9027
|
+
agentCliVersion = probeAgentCliVersion(profile?.command, {
|
|
9028
|
+
env: {
|
|
9029
|
+
...process.env,
|
|
9030
|
+
...normalizeEnvMap(profileEnv),
|
|
9031
|
+
},
|
|
9032
|
+
});
|
|
9033
|
+
} catch { /* best effort */ }
|
|
9034
|
+
};
|
|
9035
|
+
|
|
8867
9036
|
// Broker REST endpoint for live linked-resource prompts (issue #63) and the
|
|
8868
9037
|
// C8 REST source for `--auto`'s engine-read enrolment. Derived from the SAME
|
|
8869
9038
|
// client that activates jobs (its profile REST address) when no explicit
|
|
@@ -9366,7 +9535,19 @@ async function workAgent(req, flags, ctx) {
|
|
|
9366
9535
|
// job, never on these lines).
|
|
9367
9536
|
const aiCorr = `job ${job.jobKey} eik ${job.elementInstanceKey ?? '?'} pik ${job.processInstanceKey ?? '?'}`;
|
|
9368
9537
|
if (!agentInstanceOff && isExternalAgentJob(job)) {
|
|
9369
|
-
|
|
9538
|
+
maybeProbeAgentCliVersion();
|
|
9539
|
+
// #257 review: the synchronous CLI `--version` probe above can block for up
|
|
9540
|
+
// to its timeout on the FIRST external activation. A force-stop/lease-loss
|
|
9541
|
+
// can win that race while spawnSync is blocked, so re-check the setup-abort
|
|
9542
|
+
// gate before minting the durable AgentInstance — otherwise an already-aborted
|
|
9543
|
+
// run would call activate() and mint an instance/transcript that is then
|
|
9544
|
+
// immediately orphaned (the post-activate gates run too late to prevent the
|
|
9545
|
+
// create). Mirrors the pre-probe agent-instance gate above.
|
|
9546
|
+
if (checkSetupAbort(abortSignal, { jobType, jobKey: job.jobKey, stage: 'agent-instance', logger })) {
|
|
9547
|
+
if (isContainer) liveRunIds.delete(runId);
|
|
9548
|
+
return;
|
|
9549
|
+
}
|
|
9550
|
+
agentInstanceProducer = createAgentInstanceProducer({ camunda, job, profile, envelope, logger, runtimeVersion: pluginVersion, agentCliVersion });
|
|
9370
9551
|
try {
|
|
9371
9552
|
// `createAgentInstanceProducer` always returns an object — including a
|
|
9372
9553
|
// DISABLED facade when the host SDK lacks createAgentInstance/
|
|
@@ -15464,6 +15645,7 @@ export {
|
|
|
15464
15645
|
startAgenticChannelWatchdog,
|
|
15465
15646
|
};
|
|
15466
15647
|
export { compareSemver, githubRepoSlug, filterReleasesSince, renderReleaseBody };
|
|
15648
|
+
export { probeAgentCliVersion };
|
|
15467
15649
|
export {
|
|
15468
15650
|
webConsoleUrl,
|
|
15469
15651
|
consoleLinkLabel,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "c8ctl-plugin-nano",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.64.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "c8ctl plugin to start, inspect, and stop a local Nano BPM (nanobpmn) cluster",
|
|
6
6
|
"main": "c8ctl-plugin.js",
|
|
@@ -75,12 +75,12 @@
|
|
|
75
75
|
},
|
|
76
76
|
"optionalDependencies": {
|
|
77
77
|
"node-pty": "^1.0.0",
|
|
78
|
-
"@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.
|
|
79
|
-
"@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.
|
|
80
|
-
"@nanobpm/c8ctl-plugin-nano-linux-x64": "1.
|
|
81
|
-
"@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.
|
|
82
|
-
"@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.
|
|
83
|
-
"@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.
|
|
84
|
-
"@nanobpm/c8ctl-plugin-nano-win32-x64": "1.
|
|
78
|
+
"@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.64.0",
|
|
79
|
+
"@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.64.0",
|
|
80
|
+
"@nanobpm/c8ctl-plugin-nano-linux-x64": "1.64.0",
|
|
81
|
+
"@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.64.0",
|
|
82
|
+
"@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.64.0",
|
|
83
|
+
"@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.64.0",
|
|
84
|
+
"@nanobpm/c8ctl-plugin-nano-win32-x64": "1.64.0"
|
|
85
85
|
}
|
|
86
86
|
}
|