@yeaft/webchat-agent 1.0.430 → 1.0.431

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.
@@ -1 +1 @@
1
- {"version":"1.0.430"}
1
+ {"version":"1.0.431"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "1.0.430",
3
+ "version": "1.0.431",
4
4
  "description": "Remote worker agent for Yeaft Web Code Agent — connects the native Yeaft engine, CLI providers, and workbench tools",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -7,6 +7,28 @@ function policyError(message) {
7
7
  return error;
8
8
  }
9
9
 
10
+ function diagnosticValue(value) {
11
+ return String(value ?? '')
12
+ .replace(/[^a-zA-Z0-9._:-]+/g, '-')
13
+ .replace(/^-+|-+$/g, '')
14
+ .slice(0, 128) || 'none';
15
+ }
16
+
17
+ function assignmentError(message, {
18
+ policy, stageType, candidates = [], excluded = [], fallback = 'not-attempted',
19
+ }) {
20
+ const context = [
21
+ `mode=${diagnosticValue(policy.mode)}`,
22
+ `capability=${diagnosticValue(policy.capability || stageType)}`,
23
+ `fallback=${diagnosticValue(fallback)}`,
24
+ `candidates=${candidates.length
25
+ ? candidates.map(vp => diagnosticValue(vp?.id || vp)).sort().join(',')
26
+ : 'none'}`,
27
+ `excluded=${excluded.size ? [...excluded].map(diagnosticValue).sort().join(',') : 'none'}`,
28
+ ].join('; ');
29
+ return policyError(`${message} (${context})`);
30
+ }
31
+
10
32
  const CAPABILITY_TERMS = Object.freeze({
11
33
  triage: ['triage', 'requirement', 'flow', 'product', 'strategy', 'cross-domain', 'analysis'],
12
34
  implement: ['implement', 'engineer', 'developer', 'engineering', 'systems', 'code', 'execution'],
@@ -60,23 +82,46 @@ function priorVpIdsForSeparation(policy, priorRuns) {
60
82
  export function selectWorkItemVp({ policy: rawPolicy, stageType, vps, priorRuns = [] }) {
61
83
  const policy = normalizeAssignmentPolicy(rawPolicy, stageType);
62
84
  const all = Array.isArray(vps) ? vps.filter(vp => vp?.id) : [];
63
- if (all.length === 0) throw policyError('Work Center has no available VPs');
64
- const byId = new Map(all.map(vp => [vp.id, vp]));
65
85
  const excluded = priorVpIdsForSeparation(policy, priorRuns);
86
+ if (all.length === 0) {
87
+ throw assignmentError('Work Center has no available VPs', {
88
+ policy, stageType, candidates: [], excluded,
89
+ });
90
+ }
91
+ const byId = new Map(all.map(vp => [vp.id, vp]));
66
92
 
67
93
  if (policy.mode === 'fixed') {
68
94
  const fixed = byId.get(policy.fixedVpId);
69
- if (!fixed) throw policyError(`Fixed Work Center VP is unavailable: ${policy.fixedVpId}`);
70
- if (excluded.has(fixed.id)) throw policyError(`Work Center separation policy excludes VP: ${fixed.id}`);
95
+ if (!fixed) {
96
+ throw assignmentError(`Fixed Work Center VP is unavailable: ${diagnosticValue(policy.fixedVpId)}`, {
97
+ policy, stageType, candidates: [policy.fixedVpId], excluded,
98
+ });
99
+ }
100
+ if (excluded.has(fixed.id)) {
101
+ throw assignmentError(`Work Center separation policy excludes VP: ${diagnosticValue(fixed.id)}`, {
102
+ policy, stageType, candidates: [fixed], excluded,
103
+ });
104
+ }
71
105
  return { vp: fixed, reason: `fixed:${fixed.id}`, policy };
72
106
  }
73
107
 
108
+ const configuredCandidates = ['pool', 'planned'].includes(policy.mode)
109
+ ? policy.candidateVpIds
110
+ : all;
74
111
  const pool = ['pool', 'planned'].includes(policy.mode)
75
112
  ? policy.candidateVpIds.map(id => byId.get(id)).filter(Boolean)
76
113
  : all;
77
- if (pool.length === 0) throw policyError('No configured Work Center VP candidates are available');
114
+ if (pool.length === 0) {
115
+ throw assignmentError('No configured Work Center VP candidates are available', {
116
+ policy, stageType, candidates: configuredCandidates, excluded,
117
+ });
118
+ }
78
119
  const eligible = pool.filter(vp => !excluded.has(vp.id));
79
- if (eligible.length === 0) throw policyError('No Work Center VP satisfies the stage separation policy');
120
+ if (eligible.length === 0) {
121
+ throw assignmentError('No Work Center VP satisfies the stage separation policy', {
122
+ policy, stageType, candidates: pool, excluded,
123
+ });
124
+ }
80
125
  if (policy.mode === 'planned') {
81
126
  return {
82
127
  vp: eligible[0],
@@ -99,7 +144,11 @@ export function selectWorkItemVp({ policy: rawPolicy, stageType, vps, priorRuns
99
144
  policy,
100
145
  };
101
146
  }
102
- throw policyError(`No Work Center VP matches capability: ${policy.capability || stageType}`);
147
+ return {
148
+ vp: fallback[0].vp,
149
+ reason: `${policy.mode}:${policy.capability || stageType}:fallback=${stageType}:eligible-id:score=0`,
150
+ policy,
151
+ };
103
152
  }
104
153
  return {
105
154
  vp: ranked[0].vp,
@@ -0,0 +1,28 @@
1
+ import { sanitizeDiagnosticText } from './debug-projection.js';
2
+
3
+ const MAX_FAILURE_INSPECTION_LENGTH = 16_000;
4
+ const MAX_MODEL_FAILURE_DIAGNOSTIC_BYTES = 2_000;
5
+ const SAFE_FAILURE_FALLBACK = 'The Action failed. Sensitive details were omitted.';
6
+ const PROVIDER_TOKEN_PATTERN = /\b(?:sk-(?:proj-)?[A-Za-z0-9_-]{12,}|github_pat_[A-Za-z0-9_]{12,}|gh[pousr]_[A-Za-z0-9_]{12,}|xox[baprs]-[A-Za-z0-9-]{12,}|AKIA[A-Z0-9]{12,})\b/i;
7
+ const HIGH_ENTROPY_SECRET_PATTERN = /\b(?=[A-Za-z0-9_./+=-]{32,}\b)(?=[A-Za-z0-9_./+=-]*[A-Za-z])(?=[A-Za-z0-9_./+=-]*\d)[A-Za-z0-9_./+=-]+\b/;
8
+ const LOCAL_PATH_ASSIGNMENT_PATTERN = /\b(?:path|cwd|file|filename|directory)\s*[:=]\s*(?:\/(?!\/)|[A-Za-z]:\\|\\\\)/i;
9
+ const POSIX_ABSOLUTE_PATH_PATTERN = /(?:^|[\s("'`])\/(?!\/)[^\r\n]*/m;
10
+ const WINDOWS_ABSOLUTE_PATH_PATTERN = /(?:^|[\s("'`])(?:[A-Za-z]:\\|\\\\)[^\r\n]*/m;
11
+
12
+ function containsUnsafeFailureText(value) {
13
+ return PROVIDER_TOKEN_PATTERN.test(value)
14
+ || HIGH_ENTROPY_SECRET_PATTERN.test(value)
15
+ || LOCAL_PATH_ASSIGNMENT_PATTERN.test(value)
16
+ || POSIX_ABSOLUTE_PATH_PATTERN.test(value)
17
+ || WINDOWS_ABSOLUTE_PATH_PATTERN.test(value);
18
+ }
19
+
20
+ /** Project a persisted Run error into bounded, model-safe Action context. */
21
+ export function sanitizeModelFailureDiagnostic(value) {
22
+ const raw = typeof value === 'string'
23
+ ? value.trim().slice(0, MAX_FAILURE_INSPECTION_LENGTH)
24
+ : '';
25
+ if (!raw) return null;
26
+ if (containsUnsafeFailureText(raw)) return SAFE_FAILURE_FALLBACK;
27
+ return sanitizeDiagnosticText(raw, MAX_MODEL_FAILURE_DIAGNOSTIC_BYTES) || null;
28
+ }
@@ -8,6 +8,7 @@ import {
8
8
  } from './action-identity.js';
9
9
  import { sessionMessageQuotePrompt } from '../session-message-quote.js';
10
10
  import { normalizeSessionContextSnapshot } from './session-context.js';
11
+ import { sanitizeModelFailureDiagnostic } from './failure-diagnostic.js';
11
12
 
12
13
  export const MAINLINE_CONTEXT_HARD_LIMIT_BYTES = 64 * 1024;
13
14
  export const MAINLINE_CONTEXT_TARGET_MIN_BYTES = 16 * 1024;
@@ -314,6 +315,7 @@ export function buildMainlineProjection(detail) {
314
315
  outputs: normalizeOutputs(run.outputs),
315
316
  reviewDecision: run.reviewDecision || null,
316
317
  waitingReason: run.waitingReason || null,
318
+ error: sanitizeModelFailureDiagnostic(run.error),
317
319
  endedAt: run.endedAt || null,
318
320
  };
319
321
  }