ec-web 0.0.12 → 0.0.14
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/dist/sources/session-codex.js +37 -4
- package/dist/static/app.js +73 -6
- package/dist/static/index.html +1 -1
- package/dist/static/style.css +1 -1
- package/package.json +1 -1
|
@@ -430,16 +430,49 @@ function toolOutputText(output) {
|
|
|
430
430
|
return String(output);
|
|
431
431
|
}
|
|
432
432
|
}
|
|
433
|
-
function
|
|
433
|
+
function firstString(...values) {
|
|
434
|
+
return values.find((value) => typeof value === 'string' && value.trim().length > 0)?.trim();
|
|
435
|
+
}
|
|
436
|
+
function approvalAuditFromPayload(payload) {
|
|
437
|
+
const candidates = [
|
|
438
|
+
payload.approval,
|
|
439
|
+
payload.approvalAudit,
|
|
440
|
+
payload.approval_audit,
|
|
441
|
+
payload.metadata,
|
|
442
|
+
payload.meta,
|
|
443
|
+
payload,
|
|
444
|
+
].filter((value) => !!value && typeof value === 'object' && !Array.isArray(value));
|
|
445
|
+
const value = (key) => candidates.map(candidate => candidate[key]).find(candidate => candidate !== undefined);
|
|
446
|
+
const requestId = firstString(value('requestId'), value('request_id'), value('approvalRequestId'), value('approval_request_id'));
|
|
447
|
+
const method = firstString(value('method'), value('approvalMethod'), value('approval_method'));
|
|
448
|
+
const decision = firstString(value('decision'), value('approvalDecision'), value('approval_decision'));
|
|
449
|
+
const decisionSource = firstString(value('decisionSource'), value('decision_source'), value('approver'), value('approverType'));
|
|
450
|
+
const policyCode = firstString(value('policyCode'), value('policy_code'));
|
|
451
|
+
return {
|
|
452
|
+
available: !!(requestId || method || decision || decisionSource || policyCode),
|
|
453
|
+
...(requestId ? { requestId } : {}),
|
|
454
|
+
...(method ? { method } : {}),
|
|
455
|
+
...(decision ? { decision } : {}),
|
|
456
|
+
...(decisionSource ? { decisionSource } : {}),
|
|
457
|
+
...(policyCode ? { policyCode } : {}),
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
function classifyCodexToolRejection(text, payload) {
|
|
434
461
|
const normalized = text.replace(/\\(["'])/g, '$1');
|
|
435
462
|
if (!/Rejected\(\s*["']rejected by user["']\s*\)/i.test(normalized))
|
|
436
463
|
return undefined;
|
|
437
|
-
const
|
|
464
|
+
const audit = approvalAuditFromPayload(payload);
|
|
465
|
+
const source = audit.policyCode || audit.decisionSource?.toLowerCase().includes('policy')
|
|
466
|
+
? 'evolcore-policy'
|
|
467
|
+
: 'codex-approval';
|
|
438
468
|
return {
|
|
439
469
|
kind: 'approval_rejected',
|
|
440
|
-
source
|
|
470
|
+
source,
|
|
441
471
|
reason: 'rejected_by_user',
|
|
442
472
|
executed: false,
|
|
473
|
+
evidence: audit.available ? 'structured_metadata' : 'backend_text',
|
|
474
|
+
backendReason: 'Rejected("rejected by user")',
|
|
475
|
+
audit,
|
|
443
476
|
};
|
|
444
477
|
}
|
|
445
478
|
export function parseCodexToolEvent(event) {
|
|
@@ -470,7 +503,7 @@ export function parseCodexToolEvent(event) {
|
|
|
470
503
|
}
|
|
471
504
|
if (payload.type === 'function_call_output' || payload.type === 'custom_tool_call_output') {
|
|
472
505
|
const text = toolOutputText(payload.output);
|
|
473
|
-
const rejection = classifyCodexToolRejection(text);
|
|
506
|
+
const rejection = classifyCodexToolRejection(text, payload);
|
|
474
507
|
return {
|
|
475
508
|
kind: 'result',
|
|
476
509
|
callId,
|
package/dist/static/app.js
CHANGED
|
@@ -346,7 +346,13 @@ const translations = {
|
|
|
346
346
|
'sessions.block.rejection.source.policy': 'EvolCore 策略自动拦截',
|
|
347
347
|
'sessions.block.rejection.source.approval': '审批通道拒绝',
|
|
348
348
|
'sessions.block.rejection.notExecuted': '命令未执行',
|
|
349
|
-
'sessions.block.rejection.
|
|
349
|
+
'sessions.block.rejection.evidence.backend': '仅有底层文本证据,未提供审批审计',
|
|
350
|
+
'sessions.block.rejection.evidence.structured': '已提供结构化审批审计',
|
|
351
|
+
'sessions.block.rejection.audit.requestId': 'request_id',
|
|
352
|
+
'sessions.block.rejection.audit.method': 'method',
|
|
353
|
+
'sessions.block.rejection.audit.decision': 'decision',
|
|
354
|
+
'sessions.block.rejection.audit.source': 'decision_source',
|
|
355
|
+
'sessions.block.rejection.audit.policy': 'policy_code',
|
|
350
356
|
'sessions.content.expand': '展开内容',
|
|
351
357
|
'sessions.content.collapse': '收起内容',
|
|
352
358
|
'sessions.contextTitle': '最后一轮喂给模型的完整上下文大小',
|
|
@@ -1243,7 +1249,13 @@ const translations = {
|
|
|
1243
1249
|
'sessions.block.rejection.source.policy': 'EvolCore policy auto-blocked',
|
|
1244
1250
|
'sessions.block.rejection.source.approval': 'Approval channel rejected',
|
|
1245
1251
|
'sessions.block.rejection.notExecuted': 'Command was not executed',
|
|
1246
|
-
'sessions.block.rejection.
|
|
1252
|
+
'sessions.block.rejection.evidence.backend': 'Backend text only; approval audit unavailable',
|
|
1253
|
+
'sessions.block.rejection.evidence.structured': 'Structured approval audit available',
|
|
1254
|
+
'sessions.block.rejection.audit.requestId': 'request_id',
|
|
1255
|
+
'sessions.block.rejection.audit.method': 'method',
|
|
1256
|
+
'sessions.block.rejection.audit.decision': 'decision',
|
|
1257
|
+
'sessions.block.rejection.audit.source': 'decision_source',
|
|
1258
|
+
'sessions.block.rejection.audit.policy': 'policy_code',
|
|
1247
1259
|
'sessions.content.expand': 'Expand content',
|
|
1248
1260
|
'sessions.content.collapse': 'Collapse content',
|
|
1249
1261
|
'sessions.contextTitle': 'Full context size sent to the model in the last turn',
|
|
@@ -2298,6 +2310,41 @@ function renderLogFields(fields) {
|
|
|
2298
2310
|
).join('')}</span>`;
|
|
2299
2311
|
}
|
|
2300
2312
|
|
|
2313
|
+
function logEntryKey(line) {
|
|
2314
|
+
const text = `${line.file || ''}|${line.timestamp || ''}|${line.raw || ''}`;
|
|
2315
|
+
let hash = 2166136261;
|
|
2316
|
+
for (let index = 0; index < text.length; index++) {
|
|
2317
|
+
hash = Math.imul(hash ^ text.charCodeAt(index), 16777619);
|
|
2318
|
+
}
|
|
2319
|
+
return `${(hash >>> 0).toString(36)}-${text.length.toString(36)}`;
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
function captureLogScrollAnchor(stream) {
|
|
2323
|
+
if (logSel.follow || !stream) return null;
|
|
2324
|
+
const scrollTop = stream.scrollTop;
|
|
2325
|
+
const streamTop = stream.getBoundingClientRect().top;
|
|
2326
|
+
const entry = [...stream.querySelectorAll('.log-entry')]
|
|
2327
|
+
.find(item => item.getBoundingClientRect().bottom > streamTop);
|
|
2328
|
+
return {
|
|
2329
|
+
key: entry?.dataset.logKey || null,
|
|
2330
|
+
offset: entry ? entry.getBoundingClientRect().top - streamTop : 0,
|
|
2331
|
+
scrollTop,
|
|
2332
|
+
};
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
function restoreLogScrollAnchor(stream, anchor) {
|
|
2336
|
+
if (!stream || !anchor || logSel.follow) return;
|
|
2337
|
+
const entry = anchor.key
|
|
2338
|
+
? [...stream.querySelectorAll('.log-entry')].find(item => item.dataset.logKey === anchor.key)
|
|
2339
|
+
: null;
|
|
2340
|
+
if (!entry) {
|
|
2341
|
+
stream.scrollTop = anchor.scrollTop;
|
|
2342
|
+
return;
|
|
2343
|
+
}
|
|
2344
|
+
const currentOffset = entry.getBoundingClientRect().top - stream.getBoundingClientRect().top;
|
|
2345
|
+
stream.scrollTop += currentOffset - anchor.offset;
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2301
2348
|
function bindLogControls() {
|
|
2302
2349
|
const source = $('#log-source');
|
|
2303
2350
|
const level = $('#log-level');
|
|
@@ -2349,6 +2396,7 @@ function renderLogs(data) {
|
|
|
2349
2396
|
const filterResult = $('#log-filter-result');
|
|
2350
2397
|
const stream = $('#log-stream');
|
|
2351
2398
|
if (!source || !stream) return;
|
|
2399
|
+
const scrollAnchor = captureLogScrollAnchor(stream);
|
|
2352
2400
|
bindLogControls();
|
|
2353
2401
|
level.value = logSel.level;
|
|
2354
2402
|
search.value = logSel.search;
|
|
@@ -2431,7 +2479,7 @@ function renderLogs(data) {
|
|
|
2431
2479
|
const event = line.event ? `<span class="log-event">${esc(line.event)}</span>` : '';
|
|
2432
2480
|
const actor = line.actor ? `<span class="log-actor" title="${esc(line.actor)}">${esc(shortAid(line.actor))}</span>` : '';
|
|
2433
2481
|
const message = line.message || (!line.fields?.length ? line.raw : '');
|
|
2434
|
-
return `<div class="log-entry log-entry-${lineLevel}">` +
|
|
2482
|
+
return `<div class="log-entry log-entry-${lineLevel}" data-log-key="${esc(logEntryKey(line))}">` +
|
|
2435
2483
|
'<div class="log-line">' +
|
|
2436
2484
|
`<time class="log-time" title="${esc(line.timestamp || '')}">${esc(formatLogTime(line.timestamp))}</time>` +
|
|
2437
2485
|
`<span class="log-level log-level-${lineLevel}">[${esc(levelLabel)}]</span>` +
|
|
@@ -2441,6 +2489,7 @@ function renderLogs(data) {
|
|
|
2441
2489
|
'</div>';
|
|
2442
2490
|
}).join('');
|
|
2443
2491
|
if (logSel.follow) stream.scrollTop = stream.scrollHeight;
|
|
2492
|
+
else restoreLogScrollAnchor(stream, scrollAnchor);
|
|
2444
2493
|
}
|
|
2445
2494
|
|
|
2446
2495
|
// ── 工具 ──
|
|
@@ -3706,12 +3755,30 @@ function renderBlocks(blocks, options = {}) {
|
|
|
3706
3755
|
? `<button class="ctrl-btn result-load-full" type="button" data-result-key="${esc(b.resultKey)}">${t('sessions.loadFullResult') || '加载完整结果'}</button>`
|
|
3707
3756
|
: '';
|
|
3708
3757
|
const rejectionMeta = rejection
|
|
3709
|
-
?
|
|
3758
|
+
? (() => {
|
|
3759
|
+
const audit = rejection.audit && typeof rejection.audit === 'object' ? rejection.audit : {};
|
|
3760
|
+
const auditFields = [
|
|
3761
|
+
['requestId', 'requestId'],
|
|
3762
|
+
['method', 'method'],
|
|
3763
|
+
['decision', 'decision'],
|
|
3764
|
+
['decisionSource', 'source'],
|
|
3765
|
+
['policyCode', 'policy'],
|
|
3766
|
+
].filter(([key]) => typeof audit[key] === 'string' && audit[key]);
|
|
3767
|
+
const auditText = auditFields
|
|
3768
|
+
.map(([key, labelKey]) => `<span class="result-rejection-audit"><code>${t(`sessions.block.rejection.audit.${labelKey}`)}</code>=${esc(audit[key])}</span>`)
|
|
3769
|
+
.join('');
|
|
3770
|
+
const evidenceKey = rejection.evidence === 'structured_metadata' ? 'structured' : 'backend';
|
|
3771
|
+
const backendReason = typeof rejection.backendReason === 'string' && rejection.backendReason
|
|
3772
|
+
? `<span class="result-rejection-reason">${esc(rejection.backendReason)}</span>`
|
|
3773
|
+
: '';
|
|
3774
|
+
return '<div class="result-rejection-meta">' +
|
|
3710
3775
|
`<span class="result-rejection-badge">⚠ ${t('sessions.block.rejection.title')}</span>` +
|
|
3711
3776
|
`<span>${rejection.source === 'evolcore-policy' ? t('sessions.block.rejection.source.policy') : t('sessions.block.rejection.source.approval')}</span>` +
|
|
3712
3777
|
`<span>${t('sessions.block.rejection.notExecuted')}</span>` +
|
|
3713
|
-
`<span
|
|
3714
|
-
|
|
3778
|
+
`<span>${t(`sessions.block.rejection.evidence.${evidenceKey}`)}</span>` +
|
|
3779
|
+
backendReason + auditText +
|
|
3780
|
+
'</div>';
|
|
3781
|
+
})()
|
|
3715
3782
|
: '';
|
|
3716
3783
|
const resultLabel = rejection
|
|
3717
3784
|
? t('sessions.block.rejection.title')
|
package/dist/static/index.html
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
|
|
49
49
|
</nav>
|
|
50
50
|
<span style="flex:1"></span>
|
|
51
|
-
2026-08-
|
|
51
|
+
2026-08-25 01:10
|
|
52
52
|
<span id="conn-status" class="conn-status" data-i18n="status.connecting">连接中…</span>
|
|
53
53
|
<button id="lang-btn" class="theme-btn" data-i18n-title="language.buttonTitle" title="切换语言">🌐</button>
|
|
54
54
|
<button id="theme-btn" class="theme-btn" data-i18n-title="theme.buttonTitle" title="主题">🌗</button>
|
package/dist/static/style.css
CHANGED
|
@@ -199,7 +199,7 @@ body {
|
|
|
199
199
|
.log-live i { width: 7px; height: 7px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 3px color-mix(in srgb, var(--green) 15%, transparent); }
|
|
200
200
|
.log-meta { display: flex; justify-content: space-between; gap: 12px; padding: 7px 16px; color: var(--dim); font-size: 11px; border-top: 1px solid var(--border); background: var(--bg2); flex-shrink: 0; font-variant-numeric: tabular-nums; }
|
|
201
201
|
.log-meta-status { display: inline-flex; align-items: center; gap: 16px; margin-left: auto; }
|
|
202
|
-
.log-stream { flex: 1; min-height: 0; overflow: auto; padding: 6px 0 16px; background: var(--bg); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; line-height: 1.5; }
|
|
202
|
+
.log-stream { flex: 1; min-height: 0; overflow: auto; overflow-anchor: none; padding: 6px 0 16px; background: var(--bg); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; line-height: 1.5; }
|
|
203
203
|
.log-entry { border-bottom: 1px solid color-mix(in srgb, var(--border) 55%, transparent); }
|
|
204
204
|
.log-line { display: grid; grid-template-columns: 112px 56px 150px minmax(320px, 1fr); gap: 10px; padding: 4px 16px; align-items: start; }
|
|
205
205
|
.log-line:hover { background: var(--bg2); }
|