@sabaiway/agent-workflow-kit 1.45.0 → 1.46.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.
@@ -339,6 +339,44 @@ export const validateManifest = (skillDir) => {
339
339
  }
340
340
  }
341
341
 
342
+ // `writableDirs` (REC-UX-REWORK, D6): the backend CLI's writable state-dir declarations —
343
+ // {env, default} entries the Recommendations advisor RESOLVES at run time (a NON-EMPTY env
344
+ // value wins, else the default) and renders into the sandbox-lane recipe. Like `networkHosts`
345
+ // this is a DOCUMENTATION source (the kit never seeds filesystem allowances), and a malformed
346
+ // list FAILS --strict for the same reason: the resolved dir is rendered into a hand-applied line.
347
+ const writableDirs = manifest.writableDirs;
348
+ if (writableDirs != null) {
349
+ if (!Array.isArray(writableDirs) || writableDirs.length === 0) {
350
+ errors.push('`writableDirs` must be a non-empty array of {env, default} entries');
351
+ } else {
352
+ const seenDefaults = new Set();
353
+ writableDirs.forEach((entry, i) => {
354
+ const at = `\`writableDirs[${i}]\``;
355
+ if (entry == null || typeof entry !== 'object' || Array.isArray(entry)) {
356
+ errors.push(`${at} must be an {env, default} object`);
357
+ return;
358
+ }
359
+ if (entry.env !== null && (typeof entry.env !== 'string' || !/^[A-Z][A-Z0-9_]*$/.test(entry.env))) {
360
+ errors.push(`${at}.env must be null or an UPPER_SNAKE_CASE env-var name (${JSON.stringify(entry.env)})`);
361
+ }
362
+ const dir = entry.default;
363
+ if (typeof dir !== 'string' || !(dir.startsWith('~/') || dir.startsWith('/'))) {
364
+ errors.push(`${at}.default must be a \`~/\`-anchored or absolute POSIX path (${JSON.stringify(dir)})`);
365
+ return;
366
+ }
367
+ if (/[*?[\]]/.test(dir)) errors.push(`${at}.default must not carry glob characters ("${dir}")`);
368
+ // The resolved dir is rendered into a ONE-LINE hand-applied recipe — a control character
369
+ // (newline, CR, NUL, …) would break the line or the shell paste.
370
+ // eslint-disable-next-line no-control-regex
371
+ if (/[\x00-\x1f\x7f]/.test(dir)) errors.push(`${at}.default must not carry control characters (${JSON.stringify(dir)})`);
372
+ if (dir.endsWith('/')) errors.push(`${at}.default must not end with a trailing slash ("${dir}")`);
373
+ if (hasTraversal(dir)) errors.push(`${at}.default must not contain ".." traversal ("${dir}")`);
374
+ if (seenDefaults.has(dir)) errors.push(`duplicate writableDirs default "${dir}" (${at})`);
375
+ else seenDefaults.add(dir);
376
+ });
377
+ }
378
+ }
379
+
342
380
  if (!isStub) {
343
381
  const auth = readAuthoritativeVersion(skillDir);
344
382
  if (auth.version == null) errors.push(`could not resolve an authoritative version (${auth.from})`);
@@ -297,6 +297,8 @@ const costLanesAdvice = () => [
297
297
  ' • L3 frontier — judgment: plan/fold/synthesis, ADR/handover/changelog-entry wording, persuasive copy, go/no-go, real code.',
298
298
  ' • A step with no named guardrail does not move down a lane; red lines never move down (council review models · real code · memory/copy wording · the maintainer approval asks).',
299
299
  ' • Sandbox lanes (under an OS sandbox): the L0 surfaces are sandbox-safe — gates/ledger/state/fold checks, git reads, plain no-network tests; the bridge wrappers are genuinely unsandboxed (network); npm-cache-touching commands are COMMAND-SHAPE dependent — first try the sandbox-safe shape (cache under $TMPDIR, offline/notifier off). Move ONLY the failing command out of the sandbox, never its class; BATCH consecutive unsandboxed calls.',
300
+ ' • Prompt economy (autonomy-preserving dispatch): read-only fan-out (research/sweeps/extraction) runs ONLY on restricted-tool vehicles — a full-tool subagent for read-only work is a forbidden lane downgrade (invisible prompt-flood + blast radius), and a subagent is never told to shell out for facts obtainable read-only; the orchestrator\'s own shell form is ONE plain pipeline per call (a ;/&& chain or env-prefixed invocation never matches a prefix allow rule); a fan-out LAUNCHER that gates per call yields to the agent-spawn lane. Capability-gated: on a host with restricted-tool subagent vehicles use them; without restricted-tool vehicles (a host offering only full-tool agents included), read-only research stays in the orchestrator\'s own context — never a vehicle mandate a host cannot satisfy.',
301
+ ' • The prompt-economy clause narrows TOOLS for read-only work only — judgment, code, synthesis stay at the frontier lane; a task that genuinely needs to run or write keeps a full-tool subagent. Honest limit: no deterministic gate classifies a dispatch — enforcement is the canon at the point of use + the placed vehicles + the retro loop.',
300
302
  ];
301
303
 
302
304
  // The verbatim per-backend DRIVING CONTRACT block (M-contract): the exact invocation descriptor(s),