forkit-connect 0.1.12 → 0.1.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/cli.js +14 -14
- package/dist/launcher.js +34 -31
- package/dist/v1/service.d.ts +3 -0
- package/dist/v1/service.js +59 -10
- package/dist/v1/types.d.ts +2 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -90,7 +90,7 @@ const PUBLIC_COMMANDS = [
|
|
|
90
90
|
['workspace', 'List, select, or inspect optional governed workspace/project scope'],
|
|
91
91
|
['runtime', 'Register or review governed runtimes for the current repo/worktree'],
|
|
92
92
|
['register', 'Register ready local models into the current scope'],
|
|
93
|
-
['ignore', '
|
|
93
|
+
['ignore', 'Deny one detected local model on this device'],
|
|
94
94
|
['doctor', 'Run local environment diagnostics'],
|
|
95
95
|
];
|
|
96
96
|
const ADVANCED_COMMAND_GROUPS = [
|
|
@@ -139,7 +139,7 @@ function usage() {
|
|
|
139
139
|
console.log(' --no-api-key Skip automatic runtime API key creation');
|
|
140
140
|
console.log(' --dry-run Show the inferred runtime payload without creating it');
|
|
141
141
|
console.log(' --all-ready Register every ready local model in the current scope');
|
|
142
|
-
console.log(' --ignore-model <name>
|
|
142
|
+
console.log(' --ignore-model <name> Deny a detected model on this device with --ignore-digest');
|
|
143
143
|
console.log(' --ignore-digest <sha> Digest used with --ignore-model');
|
|
144
144
|
console.log(' --digest <sha> Digest used by ignore when the model name is ambiguous');
|
|
145
145
|
console.log(' --interval-seconds <n> Override daemon scan interval');
|
|
@@ -1022,7 +1022,7 @@ function buildInteractiveInboxSections(inbox) {
|
|
|
1022
1022
|
shellLine('Ready to connect', inbox.groups.ready_to_connect.length),
|
|
1023
1023
|
shellLine('Needs confirmation', inbox.groups.needs_confirmation.length),
|
|
1024
1024
|
shellLine('Connected', inbox.groups.connected.length),
|
|
1025
|
-
shellLine('
|
|
1025
|
+
shellLine('Denied on this device', inbox.groups.ignored.length),
|
|
1026
1026
|
...(inbox.summary.next_recommended_action ? [shellLine('Next action', formatSmartInboxActionValue(inbox.summary.next_recommended_action))] : []),
|
|
1027
1027
|
],
|
|
1028
1028
|
},
|
|
@@ -1044,7 +1044,7 @@ function buildInteractiveInboxItemSections(item, group) {
|
|
|
1044
1044
|
: group === 'connected'
|
|
1045
1045
|
? 'Connected'
|
|
1046
1046
|
: group === 'ignored'
|
|
1047
|
-
? '
|
|
1047
|
+
? 'Denied on this device'
|
|
1048
1048
|
: group.replaceAll('_', ' ');
|
|
1049
1049
|
const detailKeys = [
|
|
1050
1050
|
'verification_summary',
|
|
@@ -1424,7 +1424,7 @@ function formatSmartInboxActionValue(action, itemType, connectableModelName) {
|
|
|
1424
1424
|
case 'defer':
|
|
1425
1425
|
return 'Ask Later';
|
|
1426
1426
|
case 'ignore':
|
|
1427
|
-
return '
|
|
1427
|
+
return 'Deny on This Device';
|
|
1428
1428
|
default:
|
|
1429
1429
|
return action.replaceAll('_', ' ');
|
|
1430
1430
|
}
|
|
@@ -1451,7 +1451,7 @@ function printSmartInbox(inbox) {
|
|
|
1451
1451
|
printInboxGroup('Ready to Connect', inbox.groups.ready_to_connect);
|
|
1452
1452
|
printInboxGroup('Needs Confirmation', inbox.groups.needs_confirmation);
|
|
1453
1453
|
printInboxGroup('Connected', inbox.groups.connected);
|
|
1454
|
-
printInboxGroup('
|
|
1454
|
+
printInboxGroup('Denied on This Device', inbox.groups.ignored);
|
|
1455
1455
|
if (inbox.summary.next_recommended_action) {
|
|
1456
1456
|
console.log(`Next recommended action: ${formatSmartInboxActionValue(inbox.summary.next_recommended_action)}`);
|
|
1457
1457
|
}
|
|
@@ -1881,11 +1881,11 @@ async function run() {
|
|
|
1881
1881
|
if (ignoreModel && ignoreDigest) {
|
|
1882
1882
|
const ignored = service.markModelIgnored(ignoreModel, ignoreDigest);
|
|
1883
1883
|
if (!ignored) {
|
|
1884
|
-
console.error(`[forkit-connect] Model not found for
|
|
1884
|
+
console.error(`[forkit-connect] Model not found for deny-on-this-device: ${ignoreModel} ${ignoreDigest}`);
|
|
1885
1885
|
process.exitCode = 2;
|
|
1886
1886
|
return;
|
|
1887
1887
|
}
|
|
1888
|
-
console.log(`[forkit-connect]
|
|
1888
|
+
console.log(`[forkit-connect] Denied model on this device: ${ignoreModel} (${ignoreDigest})`);
|
|
1889
1889
|
return;
|
|
1890
1890
|
}
|
|
1891
1891
|
const result = await service.runDiscoveryCycle();
|
|
@@ -2840,7 +2840,7 @@ async function run() {
|
|
|
2840
2840
|
case 'connected':
|
|
2841
2841
|
return 'Connected';
|
|
2842
2842
|
case 'ignored':
|
|
2843
|
-
return '
|
|
2843
|
+
return 'Denied on this device';
|
|
2844
2844
|
default:
|
|
2845
2845
|
return group.replaceAll('_', ' ');
|
|
2846
2846
|
}
|
|
@@ -2907,21 +2907,21 @@ async function run() {
|
|
|
2907
2907
|
}
|
|
2908
2908
|
const ignored = service.markModelIgnored(model.model, model.digest);
|
|
2909
2909
|
if (!ignored) {
|
|
2910
|
-
console.error('
|
|
2910
|
+
console.error('Deny on this device failed.');
|
|
2911
2911
|
process.exitCode = 2;
|
|
2912
2912
|
return;
|
|
2913
2913
|
}
|
|
2914
|
-
console.log(`[forkit-connect]
|
|
2914
|
+
console.log(`[forkit-connect] Denied model on this device: ${model.model} (${model.digest})`);
|
|
2915
2915
|
process.exitCode = 0;
|
|
2916
2916
|
return;
|
|
2917
2917
|
}
|
|
2918
2918
|
if (item.item_type === 'runtime') {
|
|
2919
2919
|
service.markRuntimeIgnored(selector);
|
|
2920
|
-
console.log(`[forkit-connect]
|
|
2920
|
+
console.log(`[forkit-connect] Denied runtime on this device: ${item.display_name}`);
|
|
2921
2921
|
process.exitCode = 0;
|
|
2922
2922
|
return;
|
|
2923
2923
|
}
|
|
2924
|
-
console.log('[forkit-connect]
|
|
2924
|
+
console.log('[forkit-connect] Deny on this device is not available for this inbox item type from the public menu yet.');
|
|
2925
2925
|
};
|
|
2926
2926
|
const runInteractiveDeferInboxItem = (item) => {
|
|
2927
2927
|
const selector = extractInboxItemSelector(item);
|
|
@@ -4060,7 +4060,7 @@ async function run() {
|
|
|
4060
4060
|
}
|
|
4061
4061
|
const ignored = service.markModelIgnored(resolvedModel.model, resolvedModel.digest);
|
|
4062
4062
|
if (!ignored) {
|
|
4063
|
-
console.error('
|
|
4063
|
+
console.error('Deny on this device failed.');
|
|
4064
4064
|
process.exitCode = 2;
|
|
4065
4065
|
return;
|
|
4066
4066
|
}
|
package/dist/launcher.js
CHANGED
|
@@ -687,8 +687,8 @@ function buildDiscovery(service) {
|
|
|
687
687
|
let actionLabel = 'Register';
|
|
688
688
|
let actionTone = 'primary';
|
|
689
689
|
if (group === 'ignored') {
|
|
690
|
-
statusLabel = '
|
|
691
|
-
statusMeta = '
|
|
690
|
+
statusLabel = 'Denied on this device';
|
|
691
|
+
statusMeta = 'This device will keep it out of the active review queue until you reopen it.';
|
|
692
692
|
statusTone = 'muted';
|
|
693
693
|
actionLabel = 'Review';
|
|
694
694
|
actionTone = 'muted';
|
|
@@ -777,7 +777,14 @@ function buildDiscovery(service) {
|
|
|
777
777
|
let statusTone = 'ok';
|
|
778
778
|
let actionLabel = 'Register';
|
|
779
779
|
let actionTone = 'primary';
|
|
780
|
-
if (
|
|
780
|
+
if (group === 'ignored') {
|
|
781
|
+
statusLabel = 'Denied on this device';
|
|
782
|
+
statusMeta = 'This device will keep it out of the active review queue until you reopen it.';
|
|
783
|
+
statusTone = 'muted';
|
|
784
|
+
actionLabel = 'Review';
|
|
785
|
+
actionTone = 'muted';
|
|
786
|
+
}
|
|
787
|
+
else if (agent.status === 'inactive') {
|
|
781
788
|
statusLabel = 'Inactive';
|
|
782
789
|
statusMeta = 'Paused locally until the agent is seen again.';
|
|
783
790
|
statusTone = 'muted';
|
|
@@ -861,8 +868,8 @@ function buildDiscovery(service) {
|
|
|
861
868
|
statusTone = 'ok';
|
|
862
869
|
}
|
|
863
870
|
else if (group === 'ignored') {
|
|
864
|
-
statusLabel = '
|
|
865
|
-
statusMeta = '
|
|
871
|
+
statusLabel = 'Denied on this device';
|
|
872
|
+
statusMeta = 'This device will keep runtime review out of the active queue until you reopen it.';
|
|
866
873
|
statusTone = 'muted';
|
|
867
874
|
}
|
|
868
875
|
else if (group === 'needs_confirmation') {
|
|
@@ -6558,7 +6565,7 @@ function renderLauncherHtml(launcherToken) {
|
|
|
6558
6565
|
</button>
|
|
6559
6566
|
<button id="quick-review-ignore" class="danger" type="button">
|
|
6560
6567
|
<span class="quick-review-menu-copy">
|
|
6561
|
-
<span class="quick-review-menu-label">
|
|
6568
|
+
<span class="quick-review-menu-label">Deny on this device</span>
|
|
6562
6569
|
<span class="quick-review-menu-meta">Keep it out of this device review queue.</span>
|
|
6563
6570
|
</span>
|
|
6564
6571
|
</button>
|
|
@@ -6953,7 +6960,7 @@ function renderLauncherHtml(launcherToken) {
|
|
|
6953
6960
|
<div class="discovery-review-actions">
|
|
6954
6961
|
<button class="primary" id="discovery-review-primary" type="button" disabled>Review</button>
|
|
6955
6962
|
<button class="secondary" id="discovery-review-defer" type="button" disabled>Defer 24h</button>
|
|
6956
|
-
<button class="danger" id="discovery-review-ignore" type="button" disabled>
|
|
6963
|
+
<button class="danger" id="discovery-review-ignore" type="button" disabled>Deny on this device</button>
|
|
6957
6964
|
</div>
|
|
6958
6965
|
<div class="review-resolution-actions" id="discovery-review-resolution" hidden>
|
|
6959
6966
|
<button class="review-resolution-button" id="discovery-review-resolution-primary" type="button" hidden></button>
|
|
@@ -7592,11 +7599,11 @@ function renderLauncherHtml(launcherToken) {
|
|
|
7592
7599
|
}
|
|
7593
7600
|
|
|
7594
7601
|
function canReviewDefer(item) {
|
|
7595
|
-
return Boolean(item && (item.kind === 'model' || item.kind === 'runtime') && item.inboxGroup !== 'ignored');
|
|
7602
|
+
return Boolean(item && (item.kind === 'model' || item.kind === 'agent' || item.kind === 'runtime') && item.inboxGroup !== 'ignored');
|
|
7596
7603
|
}
|
|
7597
7604
|
|
|
7598
7605
|
function canReviewIgnore(item) {
|
|
7599
|
-
return Boolean(item && (item.kind === 'model' || item.kind === 'runtime') && item.inboxGroup !== 'ignored');
|
|
7606
|
+
return Boolean(item && (item.kind === 'model' || item.kind === 'agent' || item.kind === 'runtime') && item.inboxGroup !== 'ignored');
|
|
7600
7607
|
}
|
|
7601
7608
|
|
|
7602
7609
|
function getQuickReviewItems() {
|
|
@@ -8183,15 +8190,15 @@ function renderLauncherHtml(launcherToken) {
|
|
|
8183
8190
|
async function submitQuickReviewIgnore() {
|
|
8184
8191
|
const item = getQuickReviewItem();
|
|
8185
8192
|
if (!item || !canReviewIgnore(item)) {
|
|
8186
|
-
setQuickReviewStatus('
|
|
8193
|
+
setQuickReviewStatus('Deny on this device is not available for this item.', 'warn');
|
|
8187
8194
|
return;
|
|
8188
8195
|
}
|
|
8189
8196
|
const result = await postAction('/api/discovery/review/ignore', 'Ignoring this review item...', {
|
|
8190
8197
|
method: 'POST',
|
|
8191
8198
|
body: JSON.stringify({ kind: item.kind, selector: item.selector }),
|
|
8192
8199
|
});
|
|
8193
|
-
setQuickReviewStatus(result.message || 'Item
|
|
8194
|
-
setActivityMessage(result.message || 'Item
|
|
8200
|
+
setQuickReviewStatus(result.message || 'Item denied on this device.', result.ok ? 'ok' : 'warn');
|
|
8201
|
+
setActivityMessage(result.message || 'Item denied on this device.', result.ok ? 'ok' : 'warn');
|
|
8195
8202
|
await refreshAll();
|
|
8196
8203
|
resetReviewResolutionActions('quick-review');
|
|
8197
8204
|
setQuickReviewOptionsOpen(false);
|
|
@@ -8433,15 +8440,15 @@ function renderLauncherHtml(launcherToken) {
|
|
|
8433
8440
|
async function submitDiscoveryReviewIgnore() {
|
|
8434
8441
|
const item = getSelectedDiscoveryItem();
|
|
8435
8442
|
if (!item || !canReviewIgnore(item)) {
|
|
8436
|
-
setDiscoveryReviewStatus('
|
|
8443
|
+
setDiscoveryReviewStatus('Deny on this device is not available for this item.', 'warn');
|
|
8437
8444
|
return;
|
|
8438
8445
|
}
|
|
8439
8446
|
const result = await postAction('/api/discovery/review/ignore', 'Ignoring this review item...', {
|
|
8440
8447
|
method: 'POST',
|
|
8441
8448
|
body: JSON.stringify({ kind: item.kind, selector: item.selector }),
|
|
8442
8449
|
});
|
|
8443
|
-
setDiscoveryReviewStatus(result.message || 'Item
|
|
8444
|
-
setActivityMessage(result.message || 'Item
|
|
8450
|
+
setDiscoveryReviewStatus(result.message || 'Item denied on this device.', result.ok ? 'ok' : 'warn');
|
|
8451
|
+
setActivityMessage(result.message || 'Item denied on this device.', result.ok ? 'ok' : 'warn');
|
|
8445
8452
|
await refreshAll();
|
|
8446
8453
|
resetReviewResolutionActions('discovery-review');
|
|
8447
8454
|
}
|
|
@@ -9689,14 +9696,14 @@ function renderLauncherHtml(launcherToken) {
|
|
|
9689
9696
|
selectedDiscoveryId = item.id;
|
|
9690
9697
|
if (item.kind === 'runtime') {
|
|
9691
9698
|
if (item.inboxGroup === 'ignored') {
|
|
9692
|
-
setActivityMessage(item.detailSummary || item.statusMeta || (item.name + ' is currently
|
|
9699
|
+
setActivityMessage(item.detailSummary || item.statusMeta || (item.name + ' is currently denied on this device.'), 'warn');
|
|
9693
9700
|
return;
|
|
9694
9701
|
}
|
|
9695
9702
|
setView('runtime');
|
|
9696
9703
|
return;
|
|
9697
9704
|
}
|
|
9698
9705
|
if (item.inboxGroup === 'ignored') {
|
|
9699
|
-
setActivityMessage(item.detailSummary || item.statusMeta || (item.name + ' is currently
|
|
9706
|
+
setActivityMessage(item.detailSummary || item.statusMeta || (item.name + ' is currently denied on this device.'), 'warn');
|
|
9700
9707
|
return;
|
|
9701
9708
|
}
|
|
9702
9709
|
if (item.kind === 'model' && (item.actionLabel === 'Open' || item.actionLabel === 'Review')) {
|
|
@@ -9704,7 +9711,7 @@ function renderLauncherHtml(launcherToken) {
|
|
|
9704
9711
|
return;
|
|
9705
9712
|
}
|
|
9706
9713
|
if (item.kind === 'model' && item.actionLabel === 'Undo') {
|
|
9707
|
-
setActivityMessage('
|
|
9714
|
+
setActivityMessage('Restore from denied-on-this-device state is not available from the launcher yet.', 'warn');
|
|
9708
9715
|
return;
|
|
9709
9716
|
}
|
|
9710
9717
|
if (item.kind === 'model') {
|
|
@@ -9726,14 +9733,14 @@ function renderLauncherHtml(launcherToken) {
|
|
|
9726
9733
|
selectedDiscoveryId = item.id;
|
|
9727
9734
|
if (item.kind === 'runtime') {
|
|
9728
9735
|
if (item.inboxGroup === 'ignored') {
|
|
9729
|
-
setActivityMessage(item.detailSummary || item.statusMeta || (item.name + ' is currently
|
|
9736
|
+
setActivityMessage(item.detailSummary || item.statusMeta || (item.name + ' is currently denied on this device.'), 'warn');
|
|
9730
9737
|
return;
|
|
9731
9738
|
}
|
|
9732
9739
|
setView('runtime');
|
|
9733
9740
|
return;
|
|
9734
9741
|
}
|
|
9735
9742
|
if (item.inboxGroup === 'ignored') {
|
|
9736
|
-
setActivityMessage(item.detailSummary || item.statusMeta || (item.name + ' is currently
|
|
9743
|
+
setActivityMessage(item.detailSummary || item.statusMeta || (item.name + ' is currently denied on this device.'), 'warn');
|
|
9737
9744
|
return;
|
|
9738
9745
|
}
|
|
9739
9746
|
if (item.actionLabel === 'Open' || item.actionLabel === 'Review') {
|
|
@@ -9742,7 +9749,7 @@ function renderLauncherHtml(launcherToken) {
|
|
|
9742
9749
|
}
|
|
9743
9750
|
if (item.kind === 'model') {
|
|
9744
9751
|
if (item.actionLabel === 'Undo') {
|
|
9745
|
-
setActivityMessage('
|
|
9752
|
+
setActivityMessage('Restore from denied-on-this-device state is not available from the launcher yet.', 'warn');
|
|
9746
9753
|
return;
|
|
9747
9754
|
}
|
|
9748
9755
|
await openRegistrationScopeDialog(item);
|
|
@@ -11239,10 +11246,8 @@ function createLauncherApp(options) {
|
|
|
11239
11246
|
return;
|
|
11240
11247
|
}
|
|
11241
11248
|
if (kind === 'agent') {
|
|
11242
|
-
|
|
11243
|
-
|
|
11244
|
-
message: 'Agent defer is not available yet in the local review surface.',
|
|
11245
|
-
});
|
|
11249
|
+
options.service.deferDetectedAgent(selector, Number.isFinite(hours) ? hours : 24);
|
|
11250
|
+
response.json({ ok: true, message: 'Agent review deferred for 24 hours.' });
|
|
11246
11251
|
return;
|
|
11247
11252
|
}
|
|
11248
11253
|
response.status(400).json({ ok: false, message: 'Unsupported review kind.' });
|
|
@@ -11262,19 +11267,17 @@ function createLauncherApp(options) {
|
|
|
11262
11267
|
}
|
|
11263
11268
|
if (kind === 'model') {
|
|
11264
11269
|
options.service.ignoreDetectedModel(selector);
|
|
11265
|
-
response.json({ ok: true, message: 'Model review
|
|
11270
|
+
response.json({ ok: true, message: 'Model review denied on this device.' });
|
|
11266
11271
|
return;
|
|
11267
11272
|
}
|
|
11268
11273
|
if (kind === 'runtime') {
|
|
11269
11274
|
options.service.ignoreRuntimeSuggestion(selector);
|
|
11270
|
-
response.json({ ok: true, message: 'Runtime review
|
|
11275
|
+
response.json({ ok: true, message: 'Runtime review denied on this device.' });
|
|
11271
11276
|
return;
|
|
11272
11277
|
}
|
|
11273
11278
|
if (kind === 'agent') {
|
|
11274
|
-
|
|
11275
|
-
|
|
11276
|
-
message: 'Agent ignore is not available yet in the local review surface.',
|
|
11277
|
-
});
|
|
11279
|
+
options.service.ignoreDetectedAgent(selector);
|
|
11280
|
+
response.json({ ok: true, message: 'Agent review denied on this device.' });
|
|
11278
11281
|
return;
|
|
11279
11282
|
}
|
|
11280
11283
|
response.status(400).json({ ok: false, message: 'Unsupported review kind.' });
|
package/dist/v1/service.d.ts
CHANGED
|
@@ -402,6 +402,7 @@ export declare class ConnectV1Service {
|
|
|
402
402
|
private buildInboxItemId;
|
|
403
403
|
private isDeferredReviewActive;
|
|
404
404
|
private getModelReviewDisposition;
|
|
405
|
+
private getAgentReviewDisposition;
|
|
405
406
|
private getRuntimeReviewDisposition;
|
|
406
407
|
private buildModelVerificationSummary;
|
|
407
408
|
private buildRuntimeVerificationSummary;
|
|
@@ -768,7 +769,9 @@ export declare class ConnectV1Service {
|
|
|
768
769
|
runDoctorChecks(): Promise<DoctorCheck[]>;
|
|
769
770
|
markModelIgnored(modelName: string, digest: string): boolean;
|
|
770
771
|
ignoreDetectedModel(selector: string): boolean;
|
|
772
|
+
ignoreDetectedAgent(selector: string): boolean;
|
|
771
773
|
deferDetectedModel(selector: string, deferHours?: number): boolean;
|
|
774
|
+
deferDetectedAgent(selector: string, deferHours?: number): boolean;
|
|
772
775
|
markRuntimeIgnored(selector: string): boolean;
|
|
773
776
|
deferRuntimeSuggestion(selector: string, deferHours?: number): boolean;
|
|
774
777
|
ignoreRuntimeSuggestion(selector: string): boolean;
|
package/dist/v1/service.js
CHANGED
|
@@ -2153,6 +2153,15 @@ class ConnectV1Service {
|
|
|
2153
2153
|
}
|
|
2154
2154
|
return 'active';
|
|
2155
2155
|
}
|
|
2156
|
+
getAgentReviewDisposition(agent) {
|
|
2157
|
+
if (agent.review_state === 'ignored') {
|
|
2158
|
+
return 'ignored';
|
|
2159
|
+
}
|
|
2160
|
+
if (agent.review_state === 'deferred' && this.isDeferredReviewActive(agent.review_deferred_until)) {
|
|
2161
|
+
return 'deferred';
|
|
2162
|
+
}
|
|
2163
|
+
return 'active';
|
|
2164
|
+
}
|
|
2156
2165
|
getRuntimeReviewDisposition(runtimePassport) {
|
|
2157
2166
|
if (runtimePassport.review_state === 'ignored') {
|
|
2158
2167
|
return 'ignored';
|
|
@@ -3607,6 +3616,8 @@ class ConnectV1Service {
|
|
|
3607
3616
|
source_label: input.candidate.source_label ?? null,
|
|
3608
3617
|
detection_reason: input.candidate.reason ?? null,
|
|
3609
3618
|
},
|
|
3619
|
+
review_state: input.existing?.review_state,
|
|
3620
|
+
review_deferred_until: input.existing?.review_deferred_until ?? null,
|
|
3610
3621
|
};
|
|
3611
3622
|
}
|
|
3612
3623
|
queueAgentC2Event(agent, eventType, metadata = {}, occurredAt) {
|
|
@@ -4434,29 +4445,35 @@ class ConnectV1Service {
|
|
|
4434
4445
|
const sourceLabel = typeof metadata.source_label === 'string' && metadata.source_label.trim()
|
|
4435
4446
|
? metadata.source_label.trim()
|
|
4436
4447
|
: null;
|
|
4437
|
-
const
|
|
4448
|
+
const agentReviewStatus = link
|
|
4438
4449
|
? 'linked_agent'
|
|
4439
4450
|
: agent.agent_type === 'unknown_ai_tool'
|
|
4440
4451
|
? 'unknown_ai_tool'
|
|
4441
4452
|
: agent.detected_at === agent.last_seen_at
|
|
4442
4453
|
? 'new_agent'
|
|
4443
4454
|
: 'known_agent';
|
|
4455
|
+
const reviewDisposition = this.getAgentReviewDisposition(agent);
|
|
4456
|
+
if (reviewDisposition === 'deferred') {
|
|
4457
|
+
continue;
|
|
4458
|
+
}
|
|
4444
4459
|
const agentObservation = this.buildAgentObservationStatus(agent);
|
|
4445
|
-
const group =
|
|
4460
|
+
const group = reviewDisposition === 'ignored'
|
|
4446
4461
|
? 'ignored'
|
|
4447
|
-
:
|
|
4448
|
-
? '
|
|
4449
|
-
:
|
|
4450
|
-
? '
|
|
4451
|
-
:
|
|
4462
|
+
: agent.status === 'inactive'
|
|
4463
|
+
? 'ignored'
|
|
4464
|
+
: link
|
|
4465
|
+
? 'connected'
|
|
4466
|
+
: candidate.model
|
|
4467
|
+
? 'needs_confirmation'
|
|
4468
|
+
: 'ready_to_connect';
|
|
4452
4469
|
const observedWorkspaceId = normalizeDisplayText(metadata.observation_workspace_id ?? metadata.workspaceId ?? metadata.workspace_id);
|
|
4453
4470
|
const observedProjectId = normalizeDisplayText(metadata.observation_project_id ?? metadata.projectId ?? metadata.project_id);
|
|
4454
4471
|
const scopeSuggestion = this.resolveScopeSuggestion(currentState, observedWorkspaceId, observedProjectId);
|
|
4455
4472
|
const itemId = this.buildInboxItemId('agent', agent.agent_id);
|
|
4456
4473
|
const recommendedAction = link ? 'open_on_forkit' : 'link_agent_to_model';
|
|
4457
4474
|
const allowedActions = this.filterInboxActionsForBinding(link
|
|
4458
|
-
? ['open_on_forkit', 'ignore']
|
|
4459
|
-
: ['link_agent_to_model', 'ignore', 'open_on_forkit'], currentState);
|
|
4475
|
+
? ['open_on_forkit', 'defer', 'ignore']
|
|
4476
|
+
: ['link_agent_to_model', 'defer', 'ignore', 'open_on_forkit'], currentState);
|
|
4460
4477
|
if (scopeSuggestion.scope_mismatch_detected && scopeSuggestion.scope_mismatch_reason) {
|
|
4461
4478
|
this.maybeRecordScopeMismatchEvidence(currentState, {
|
|
4462
4479
|
itemType: 'agent',
|
|
@@ -4501,7 +4518,8 @@ class ConnectV1Service {
|
|
|
4501
4518
|
last_observed_tool_at: agentObservation.lastObservedToolAt,
|
|
4502
4519
|
last_tool_decision: agentObservation.lastToolDecision,
|
|
4503
4520
|
source_label: sourceLabel,
|
|
4504
|
-
review_disposition:
|
|
4521
|
+
review_disposition: agentReviewStatus,
|
|
4522
|
+
local_review_disposition: reviewDisposition,
|
|
4505
4523
|
scope_suggestion: scopeSuggestion.scope_suggestion,
|
|
4506
4524
|
scope_suggestion_kind: scopeSuggestion.scope_suggestion_kind,
|
|
4507
4525
|
scope_suggestion_source_label: scopeSuggestion.scope_suggestion_source_label,
|
|
@@ -9024,6 +9042,21 @@ class ConnectV1Service {
|
|
|
9024
9042
|
const model = this.resolveModelSelection(selector, state);
|
|
9025
9043
|
return this.markModelIgnored(model.model, model.digest);
|
|
9026
9044
|
}
|
|
9045
|
+
ignoreDetectedAgent(selector) {
|
|
9046
|
+
const state = this.stateStore.readState();
|
|
9047
|
+
const agent = this.findAgentByIdOrName(state, selector);
|
|
9048
|
+
if (!agent)
|
|
9049
|
+
return false;
|
|
9050
|
+
const nextAgents = state.detected_agents.map((item) => item.agent_id === agent.agent_id
|
|
9051
|
+
? {
|
|
9052
|
+
...item,
|
|
9053
|
+
review_state: 'ignored',
|
|
9054
|
+
review_deferred_until: null,
|
|
9055
|
+
}
|
|
9056
|
+
: item);
|
|
9057
|
+
this.stateStore.replaceDetectedAgents(nextAgents);
|
|
9058
|
+
return true;
|
|
9059
|
+
}
|
|
9027
9060
|
deferDetectedModel(selector, deferHours = 24) {
|
|
9028
9061
|
const state = this.stateStore.readState();
|
|
9029
9062
|
const model = this.resolveModelSelection(selector, state);
|
|
@@ -9038,6 +9071,22 @@ class ConnectV1Service {
|
|
|
9038
9071
|
this.stateStore.replaceDetectedModels(nextModels);
|
|
9039
9072
|
return true;
|
|
9040
9073
|
}
|
|
9074
|
+
deferDetectedAgent(selector, deferHours = 24) {
|
|
9075
|
+
const state = this.stateStore.readState();
|
|
9076
|
+
const agent = this.findAgentByIdOrName(state, selector);
|
|
9077
|
+
if (!agent)
|
|
9078
|
+
return false;
|
|
9079
|
+
const deferredUntil = new Date(Date.now() + Math.max(1, deferHours) * 60 * 60 * 1000).toISOString();
|
|
9080
|
+
const nextAgents = state.detected_agents.map((item) => item.agent_id === agent.agent_id
|
|
9081
|
+
? {
|
|
9082
|
+
...item,
|
|
9083
|
+
review_state: 'deferred',
|
|
9084
|
+
review_deferred_until: deferredUntil,
|
|
9085
|
+
}
|
|
9086
|
+
: item);
|
|
9087
|
+
this.stateStore.replaceDetectedAgents(nextAgents);
|
|
9088
|
+
return true;
|
|
9089
|
+
}
|
|
9041
9090
|
markRuntimeIgnored(selector) {
|
|
9042
9091
|
const state = this.stateStore.readState();
|
|
9043
9092
|
const runtimePassport = this.resolveRuntimePassportSelection(selector, state);
|
package/dist/v1/types.d.ts
CHANGED
|
@@ -569,6 +569,8 @@ export interface DetectedAgent {
|
|
|
569
569
|
persistence?: 'temporary' | 'permanent';
|
|
570
570
|
discovery_sources: AgentDiscoverySource[];
|
|
571
571
|
metadata: Record<string, unknown>;
|
|
572
|
+
review_state?: ReviewDisposition | undefined;
|
|
573
|
+
review_deferred_until?: string | null;
|
|
572
574
|
}
|
|
573
575
|
/**
|
|
574
576
|
* AgentLink (M2: Agent Class Metadata in linkage)
|