Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/gep/candidates.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function extractCapabilityCandidates({ recentSessionTranscript, signals }) {
];

for (const sc of signalCandidates) {
if (!signalList.includes(sc.signal)) continue;
if (!signalList.some(s => s === sc.signal || s.startsWith(sc.signal + ':'))) continue;
const evidence = `Signal present: ${sc.signal}`;
const shape = buildFiveQuestionsShape({ title: sc.title, signals, evidence });
candidates.push({
Expand Down
4 changes: 3 additions & 1 deletion src/gep/mutation.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ var OPPORTUNITY_SIGNALS = [
function hasOpportunitySignal(signals) {
var list = Array.isArray(signals) ? signals.map(function (s) { return String(s || ''); }) : [];
for (var i = 0; i < OPPORTUNITY_SIGNALS.length; i++) {
if (list.includes(OPPORTUNITY_SIGNALS[i])) return true;
var name = OPPORTUNITY_SIGNALS[i];
if (list.includes(name)) return true;
if (list.some(function (s) { return s.startsWith(name + ':'); })) return true;
}
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/gep/questionGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ function generateQuestions(opts) {
}

// --- Strategy 5: User feature requests the agent can amplify ---
if (signalSet.has('user_feature_request')) {
if (signalSet.has('user_feature_request') || signals.some(function (s) { return String(s).startsWith('user_feature_request:'); })) {
var featureLines = transcript.split('\n').filter(function(l) {
return /\b(add|implement|create|build|i want|i need|please add)\b/i.test(l);
});
Expand Down
103 changes: 84 additions & 19 deletions src/gep/signals.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ var OPPORTUNITY_SIGNALS = [
function hasOpportunitySignal(signals) {
var list = Array.isArray(signals) ? signals : [];
for (var i = 0; i < OPPORTUNITY_SIGNALS.length; i++) {
if (list.includes(OPPORTUNITY_SIGNALS[i])) return true;
var name = OPPORTUNITY_SIGNALS[i];
if (list.includes(name)) return true;
// Signals may carry extra as "name:snippet"
if (list.some(function (s) { return String(s).startsWith(name + ':'); })) return true;
}
return false;
}
Expand Down Expand Up @@ -49,8 +52,12 @@ function analyzeRecentHistory(recentEvents) {
var sigs = Array.isArray(evt.signals) ? evt.signals : [];
for (var k = 0; k < sigs.length; k++) {
var s = String(sigs[k]);
// Normalize: ignore errsig details for frequency counting
var key = s.startsWith('errsig:') ? 'errsig' : s.startsWith('recurring_errsig') ? 'recurring_errsig' : s;
// Normalize: strip details suffix so frequency keys match dedup filter keys
var key = s.startsWith('errsig:') ? 'errsig'
: s.startsWith('recurring_errsig') ? 'recurring_errsig'
: s.startsWith('user_feature_request:') ? 'user_feature_request'
: s.startsWith('user_improvement_suggestion:') ? 'user_improvement_suggestion'
: s;
signalFreq[key] = (signalFreq[key] || 0) + 1;
}
var genes = Array.isArray(evt.genes_used) ? evt.genes_used : [];
Expand Down Expand Up @@ -137,15 +144,15 @@ function extractSignals({ recentSessionTranscript, todayLog, memorySnippet, user
String(userSnippet || ''),
].join('\n');
var lower = corpus.toLowerCase();

// Analyze recent evolution history for de-duplication
var history = analyzeRecentHistory(recentEvents || []);

// --- Defensive signals (errors, missing resources) ---

// Refined error detection regex to avoid false positives on "fail"/"failed" in normal text.
// We prioritize structured error markers ([error], error:, exception:) and specific JSON patterns.
var errorHit = /\[error\]|error:|exception:|iserror":true|"status":\s*"error"|"status":\s*"failed"/.test(lower);
// Chinese: 错误、异常、失败、报错 (common in logs and stack traces).
var errorHit = /\[error\]|error:|exception:|iserror":true|"status":\s*"error"|"status":\s*"failed"|错误|异常\s*[::]|报错|失败\s*[::]/.test(lower);
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
if (errorHit) signals.push('log_error');

// Error signature (more reproducible than a coarse "log_error" tag).
Expand All @@ -156,7 +163,7 @@ function extractSignals({ recentSessionTranscript, todayLog, memorySnippet, user
.filter(Boolean);

var errLine =
lines.find(function (l) { return /\b(typeerror|referenceerror|syntaxerror)\b\s*:|error\s*:|exception\s*:|\[error/i.test(l); }) ||
lines.find(function (l) { return /\b(typeerror|referenceerror|syntaxerror)\b\s*:|error\s*:|exception\s*:|\[error|错误|异常\s*[::]|报错|失败\s*[::]/i.test(l); }) ||
null;

if (errLine) {
Expand Down Expand Up @@ -200,27 +207,77 @@ function extractSignals({ recentSessionTranscript, todayLog, memorySnippet, user
}

// --- Opportunity signals (innovation / feature requests) ---

// user_feature_request: user explicitly asks for a new capability
// Look for action verbs + object patterns that indicate a feature request
if (/\b(add|implement|create|build|make|develop|write|design)\b[^.?!\n]{3,60}\b(feature|function|module|capability|tool|support|endpoint|command|option|mode)\b/i.test(corpus)) {
signals.push('user_feature_request');
// Support 4 languages: 简中、繁中、英、日. Attach extra info (snippet) for selector/prompt use.

var featureRequestSnippet = '';
// English
var featEn = corpus.match(/\b(add|implement|create|build|make|develop|write|design)\b[^.?!\n]{3,120}\b(feature|function|module|capability|tool|support|endpoint|command|option|mode)\b/i);
if (featEn) featureRequestSnippet = featEn[0].replace(/\s+/g, ' ').trim().slice(0, 200);
if (!featureRequestSnippet && /\b(i want|i need|we need|please add|can you add|could you add|let'?s add)\b/i.test(lower)) {
var featWant = corpus.match(/.{0,80}\b(i want|i need|we need|please add|can you add|could you add|let'?s add)\b.{0,80}/i);
featureRequestSnippet = featWant ? featWant[0].replace(/\s+/g, ' ').trim().slice(0, 200) : 'feature request';
}
// 简中(含「我想……」:截取描述至 200 字)
if (!featureRequestSnippet && /加个|实现一下|做个|想要\s*一个|需要\s*一个|帮我加|帮我开发|加一下|新增一个|加个功能|做个功能|我想/.test(corpus)) {
var featZh = corpus.match(/.{0,100}(加个|实现一下|做个|想要\s*一个|需要\s*一个|帮我加|帮我开发|加一下|新增一个|加个功能|做个功能).{0,100}/);
if (featZh) featureRequestSnippet = featZh[0].replace(/\s+/g, ' ').trim().slice(0, 200);
if (!featureRequestSnippet && /我想/.test(corpus)) {
var featWantZh = corpus.match(/我想\s*[,,\.。、\s]*([\s\S]{0,400})/);
featureRequestSnippet = featWantZh ? (featWantZh[1].replace(/\s+/g, ' ').trim().slice(0, 200) || '功能需求') : '功能需求';
}
if (!featureRequestSnippet) featureRequestSnippet = '功能需求';
}
// Also catch direct "I want/need X" patterns
if (/\b(i want|i need|we need|please add|can you add|could you add|let'?s add)\b/i.test(lower)) {
signals.push('user_feature_request');
// 繁中
if (!featureRequestSnippet && /加個|實現一下|做個|想要一個|請加|新增一個|加個功能|做個功能|幫我加/.test(corpus)) {
var featTw = corpus.match(/.{0,100}(加個|實現一下|做個|想要一個|請加|新增一個|加個功能|做個功能|幫我加).{0,100}/);
featureRequestSnippet = featTw ? featTw[0].replace(/\s+/g, ' ').trim().slice(0, 200) : '功能需求';
}
// 日
if (!featureRequestSnippet && /追加|実装|作って|機能を|追加して|が欲しい|を追加|してほしい/.test(corpus)) {
var featJa = corpus.match(/.{0,100}(追加|実装|作って|機能を|追加して|が欲しい|を追加|してほしい).{0,100}/);
featureRequestSnippet = featJa ? featJa[0].replace(/\s+/g, ' ').trim().slice(0, 200) : '機能要望';
}
if (featureRequestSnippet || /\b(add|implement|create|build|make|develop|write|design)\b[^.?!\n]{3,60}\b(feature|function|module|capability|tool|support|endpoint|command|option|mode)\b/i.test(corpus) ||
/\b(i want|i need|we need|please add|can you add|could you add|let'?s add)\b/i.test(lower) ||
/加个|实现一下|做个|想要\s*一个|需要\s*一个|帮我加|帮我开发|加一下|新增一个|加个功能|做个功能|我想/.test(corpus) ||
/加個|實現一下|做個|想要一個|請加|新增一個|加個功能|做個功能|幫我加/.test(corpus) ||
/追加|実装|作って|機能を|追加して|が欲しい|を追加|してほしい/.test(corpus)) {
signals.push('user_feature_request:' + (featureRequestSnippet || ''));
Comment thread
cursor[bot] marked this conversation as resolved.
}

// user_improvement_suggestion: user suggests making something better
if (/\b(should be|could be better|improve|enhance|upgrade|refactor|clean up|simplify|streamline)\b/i.test(lower)) {
// Only fire if there is no active error (to distinguish from repair requests)
if (!errorHit) signals.push('user_improvement_suggestion');
// user_improvement_suggestion: 4 languages + extra
var improvementSnippet = '';
if (!errorHit) {
var impEn = corpus.match(/.{0,80}\b(should be|could be better|improve|enhance|upgrade|refactor|clean up|simplify|streamline)\b.{0,80}/i);
if (impEn) improvementSnippet = impEn[0].replace(/\s+/g, ' ').trim().slice(0, 200);
Comment thread
cursor[bot] marked this conversation as resolved.
if (!improvementSnippet && /改进一下|优化一下|简化|重构|整理一下|弄得更好/.test(corpus)) {
var impZh = corpus.match(/.{0,100}(改进一下|优化一下|简化|重构|整理一下|弄得更好).{0,100}/);
improvementSnippet = impZh ? impZh[0].replace(/\s+/g, ' ').trim().slice(0, 200) : '改进建议';
}
if (!improvementSnippet && /改進一下|優化一下|簡化|重構|整理一下|弄得更好/.test(corpus)) {
var impTw = corpus.match(/.{0,100}(改進一下|優化一下|簡化|重構|整理一下|弄得更好).{0,100}/);
improvementSnippet = impTw ? impTw[0].replace(/\s+/g, ' ').trim().slice(0, 200) : '改進建議';
}
if (!improvementSnippet && /改善|最適化|簡素化|リファクタ|良くして|改良/.test(corpus)) {
var impJa = corpus.match(/.{0,100}(改善|最適化|簡素化|リファクタ|良くして|改良).{0,100}/);
improvementSnippet = impJa ? impJa[0].replace(/\s+/g, ' ').trim().slice(0, 200) : '改善要望';
}
var hasImprovement = improvementSnippet ||
/\b(should be|could be better|improve|enhance|upgrade|refactor|clean up|simplify|streamline)\b/i.test(lower) ||
/改进一下|优化一下|简化|重构|整理一下|弄得更好/.test(corpus) ||
/改進一下|優化一下|簡化|重構|整理一下|弄得更好/.test(corpus) ||
/改善|最適化|簡素化|リファクタ|良くして|改良/.test(corpus);
if (hasImprovement) signals.push('user_improvement_suggestion:' + (improvementSnippet || ''));
}

// perf_bottleneck: performance issues detected
if (/\b(slow|timeout|timed?\s*out|latency|bottleneck|took too long|performance issue|high cpu|high memory|oom|out of memory)\b/i.test(lower)) {
signals.push('perf_bottleneck');
}
// Chinese: 慢/超时/卡顿/性能/内存溢出
if (/太慢|超时|卡顿|性能问题|内存溢出|跑不动|很慢/.test(corpus)) {
signals.push('perf_bottleneck');
}

// capability_gap: something is explicitly unsupported or missing
if (/\b(not supported|cannot|doesn'?t support|no way to|missing feature|unsupported|not available|not implemented|no support for)\b/i.test(lower)) {
Expand All @@ -229,6 +286,12 @@ function extractSignals({ recentSessionTranscript, todayLog, memorySnippet, user
signals.push('capability_gap');
}
}
// Chinese: 不支持/没法/无法/没有这个功能
if (/不支持|没法|无法\s*实现|没有这个功能|还不支持/.test(corpus)) {
if (!signals.includes('memory_missing') && !signals.includes('user_missing') && !signals.includes('session_logs_missing')) {
signals.push('capability_gap');
}
}

// --- Tool Usage Analytics ---
var toolUsage = {};
Expand Down Expand Up @@ -273,7 +336,9 @@ function extractSignals({ recentSessionTranscript, todayLog, memorySnippet, user
var beforeDedup = signals.length;
signals = signals.filter(function (s) {
// Normalize signal key for comparison
var key = s.startsWith('errsig:') ? 'errsig' : s.startsWith('recurring_errsig') ? 'recurring_errsig' : s;
var key = s.startsWith('errsig:') ? 'errsig' : s.startsWith('recurring_errsig') ? 'recurring_errsig'
: s.startsWith('user_feature_request:') ? 'user_feature_request' : s.startsWith('user_improvement_suggestion:') ? 'user_improvement_suggestion'
: s;
Comment thread
cursor[bot] marked this conversation as resolved.
return !history.suppressedSignals.has(key);
});
if (beforeDedup > 0 && signals.length === 0) {
Expand Down
15 changes: 14 additions & 1 deletion test/selector.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const GENES = [
type: 'Gene',
id: 'gene_innovate',
category: 'innovate',
signals_match: ['user_feature_request', 'capability_gap', 'stable_success_plateau'],
signals_match: ['user_feature_request', 'user_improvement_suggestion', 'capability_gap', 'stable_success_plateau'],
strategy: ['build it'],
validation: ['node -e "true"'],
},
Expand Down Expand Up @@ -81,6 +81,19 @@ describe('selectGene', () => {
// With preference, it should be selected even if gene_repair scores higher
assert.equal(result.selected.id, 'gene_optimize');
});

it('matches gene when signal carries extra info (user_feature_request:snippet)', () => {
// Signal format: "user_feature_request:加个支付模块" — selector matches by substring includes(pattern)
const result = selectGene(GENES, ['user_feature_request:加个支付模块,要支持微信和支付宝'], {});
assert.ok(result.selected, 'should select a gene');
assert.equal(result.selected.id, 'gene_innovate', 'innovate gene has signals_match user_feature_request');
});

it('matches gene when signal carries extra info (user_improvement_suggestion:snippet)', () => {
const result = selectGene(GENES, ['user_improvement_suggestion:refactor the payment module and simplify the API'], {});
assert.ok(result.selected);
assert.equal(result.selected.id, 'gene_innovate', 'innovate gene has signals_match user_improvement_suggestion');
});
});

describe('selectCapsule', () => {
Expand Down
Loading