@aiassesstech/nole 0.6.2 → 0.7.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.
- package/CHANGELOG.md +55 -0
- package/dist/freedom-run/freedom-run-manager.d.ts +57 -0
- package/dist/freedom-run/freedom-run-manager.d.ts.map +1 -0
- package/dist/freedom-run/freedom-run-manager.js +186 -0
- package/dist/freedom-run/freedom-run-manager.js.map +1 -0
- package/dist/freedom-run/index.d.ts +2 -0
- package/dist/freedom-run/index.d.ts.map +1 -0
- package/dist/freedom-run/index.js +2 -0
- package/dist/freedom-run/index.js.map +1 -0
- package/dist/governance/types.d.ts +1 -1
- package/dist/governance/types.d.ts.map +1 -1
- package/dist/governance/types.js.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +448 -30
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
package/dist/plugin.js
CHANGED
|
@@ -25,6 +25,7 @@ import { extractMoltBookCandidates } from './pipeline/discovery.js';
|
|
|
25
25
|
import { extractDirectoryCandidates, prioritizeDirectoryOutreach } from './pipeline/directory-outreach.js';
|
|
26
26
|
import { generateDailyContentPlan } from './pipeline/content-calendar.js';
|
|
27
27
|
import { getAllDiscoveryKeywords } from './pipeline/types.js';
|
|
28
|
+
import { FreedomRunManager } from './freedom-run/index.js';
|
|
28
29
|
/**
|
|
29
30
|
* OpenClaw plugin entry point — export default function register(api)
|
|
30
31
|
*
|
|
@@ -103,6 +104,17 @@ export default function register(api) {
|
|
|
103
104
|
serviceRegistry.register(moltbookResilient);
|
|
104
105
|
serviceRegistry.register(xResilient);
|
|
105
106
|
const postQueue = new PostQueue({ dataDir, maxQueueSize: 100, maxRetries: 5, maxAgeMs: 24 * 60 * 60 * 1000 });
|
|
107
|
+
// ── Freedom Run Manager (BB Amendment A1) ──
|
|
108
|
+
const freedomRunDeps = {
|
|
109
|
+
getPipelineSnapshot: () => pipeline.getSummary(),
|
|
110
|
+
getAuditTail: (limit) => store.getAuditChain(limit),
|
|
111
|
+
getWalletBalance: () => wallet.getBalance(),
|
|
112
|
+
getServiceHealth: () => serviceRegistry.getSnapshot(),
|
|
113
|
+
};
|
|
114
|
+
const freedomRun = new FreedomRunManager(dataDir, freedomRunDeps);
|
|
115
|
+
freedomRun.initialize().catch((err) => {
|
|
116
|
+
console.warn(`[nole] Freedom run init warning: ${err.message}`);
|
|
117
|
+
});
|
|
106
118
|
// Wallet adapter (auto-detects Coinbase credentials from ~/.nole/credentials)
|
|
107
119
|
let wallet = new MockWalletAdapter(config.seedCapitalUsd);
|
|
108
120
|
// Platform API client — initialized after credentials load (see below)
|
|
@@ -2631,11 +2643,17 @@ export default function register(api) {
|
|
|
2631
2643
|
});
|
|
2632
2644
|
api.registerTool({
|
|
2633
2645
|
name: "nole_daily_brief",
|
|
2634
|
-
description: "
|
|
2635
|
-
"
|
|
2646
|
+
description: "Nole's daily operations brief with windowed scheduling for Freedom Run. " +
|
|
2647
|
+
"Windows: morning (scan/qualify), midday (deliver/post), afternoon (nurture/commission), " +
|
|
2648
|
+
"evening (report/metrics). Use 'auto' for full one-shot brief.",
|
|
2636
2649
|
parameters: {
|
|
2637
2650
|
type: "object",
|
|
2638
2651
|
properties: {
|
|
2652
|
+
window: {
|
|
2653
|
+
type: "string",
|
|
2654
|
+
enum: ["morning", "midday", "afternoon", "evening", "auto"],
|
|
2655
|
+
description: "Which daily cycle window to execute. Defaults to 'auto' (full brief).",
|
|
2656
|
+
},
|
|
2639
2657
|
includeProspects: {
|
|
2640
2658
|
type: "boolean",
|
|
2641
2659
|
description: "Include detailed prospect list in brief (default: false)",
|
|
@@ -2646,8 +2664,257 @@ export default function register(api) {
|
|
|
2646
2664
|
const dbv = await validateToolParams("nole_daily_brief", _toolCallId, params);
|
|
2647
2665
|
if (!dbv.ok)
|
|
2648
2666
|
return validationErrorResponse(dbv.message);
|
|
2667
|
+
const window = params.window || 'auto';
|
|
2668
|
+
// Freedom Run gate — if not 'auto' mode, require running state (BB Amendment A1)
|
|
2669
|
+
if (window !== 'auto') {
|
|
2670
|
+
const frStatus = freedomRun.getStatus();
|
|
2671
|
+
if (!freedomRun.canOperate()) {
|
|
2672
|
+
return {
|
|
2673
|
+
content: [{
|
|
2674
|
+
type: "text",
|
|
2675
|
+
text: JSON.stringify({
|
|
2676
|
+
freedomRunGate: 'BLOCKED',
|
|
2677
|
+
status: frStatus.status,
|
|
2678
|
+
reason: frStatus.reason,
|
|
2679
|
+
triggeredBy: frStatus.triggeredBy,
|
|
2680
|
+
message: frStatus.status === 'terminated'
|
|
2681
|
+
? 'Freedom Run has been terminated. A new spec is required to restart.'
|
|
2682
|
+
: 'Freedom Run is paused. Jessie or Greg must resume before daily operations continue.',
|
|
2683
|
+
}, null, 2),
|
|
2684
|
+
}],
|
|
2685
|
+
};
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2649
2688
|
try {
|
|
2689
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
2650
2690
|
const pipelineSummary = await pipeline.getSummary();
|
|
2691
|
+
// ── Morning window: scan, qualify, conversion detection, veto rate check ──
|
|
2692
|
+
if (window === 'morning' || window === 'auto') {
|
|
2693
|
+
const walletBalance = await wallet.getBalance().catch(() => null);
|
|
2694
|
+
// Conversion detection — poll for pitched prospects that subscribed (§11.7)
|
|
2695
|
+
const pitched = await pipeline.listByStage('pitched');
|
|
2696
|
+
const conversions = [];
|
|
2697
|
+
for (const prospect of pitched) {
|
|
2698
|
+
if (!prospect.walletAddress)
|
|
2699
|
+
continue;
|
|
2700
|
+
try {
|
|
2701
|
+
const sub = await platform.getSubscription(prospect.walletAddress);
|
|
2702
|
+
if (sub.ok && sub.data) {
|
|
2703
|
+
await pipeline.updateStage(prospect.id, 'converted', {
|
|
2704
|
+
convertedAt: new Date().toISOString(),
|
|
2705
|
+
subscriptionTier: sub.data.tier,
|
|
2706
|
+
});
|
|
2707
|
+
conversions.push({ agentName: prospect.agentName, tier: sub.data.tier ?? 'unknown' });
|
|
2708
|
+
// Send onboarding message
|
|
2709
|
+
const convertedProspect = await pipeline.get(prospect.id) ?? prospect;
|
|
2710
|
+
const onboardMsg = generateOnboardingMessage(convertedProspect, 'https://www.aiassesstech.com/assess', 'https://www.aiassesstech.com/directory');
|
|
2711
|
+
if (prospect.discoveryChannel === 'moltbook' && moltbook) {
|
|
2712
|
+
await moltbook.createPost('aiassesstech', onboardMsg.subject, onboardMsg.body).catch(() => { });
|
|
2713
|
+
}
|
|
2714
|
+
// Submit first assessment trigger through governance (BB Amendment A9)
|
|
2715
|
+
await governance.propose({
|
|
2716
|
+
actionType: 'self_assessment',
|
|
2717
|
+
description: `Trigger first Grillo assessment for new subscriber ${prospect.agentName} (${prospect.walletAddress})`,
|
|
2718
|
+
riskLevel: 'low',
|
|
2719
|
+
metadata: { targetAgent: prospect.agentName, walletAddress: prospect.walletAddress },
|
|
2720
|
+
}).catch((err) => {
|
|
2721
|
+
console.warn(`[nole] First assessment governance proposal failed: ${err.message}`);
|
|
2722
|
+
});
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
catch { /* polling failure is non-fatal */ }
|
|
2726
|
+
}
|
|
2727
|
+
// Veto rate check for Freedom Run kill switch (BB Amendment A2, §11.8)
|
|
2728
|
+
let vetoRateWarning = null;
|
|
2729
|
+
try {
|
|
2730
|
+
const govStats = await governance.getStats();
|
|
2731
|
+
const recentResults = await store.listGovernanceResults(10);
|
|
2732
|
+
const recentVetoes = recentResults.filter(r => r.finalOutcome === 'vetoed').length;
|
|
2733
|
+
if (recentResults.length >= 5) {
|
|
2734
|
+
const last5 = recentResults.slice(0, 5);
|
|
2735
|
+
const last5Vetoes = last5.filter(r => r.finalOutcome === 'vetoed').length;
|
|
2736
|
+
if (last5Vetoes / 5 >= 0.6) {
|
|
2737
|
+
vetoRateWarning = `CRITICAL: 60% veto rate over last 5 proposals — auto-terminating Freedom Run`;
|
|
2738
|
+
await freedomRun.terminate(`Grillo veto rate ${(last5Vetoes / 5 * 100).toFixed(0)}% over 5-pitch window exceeds 60% threshold`, 'mighty-mark');
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
if (!vetoRateWarning && recentResults.length >= 10) {
|
|
2742
|
+
const last10 = recentResults.slice(0, 10);
|
|
2743
|
+
const last10Vetoes = last10.filter(r => r.finalOutcome === 'vetoed').length;
|
|
2744
|
+
if (last10Vetoes / 10 >= 0.4) {
|
|
2745
|
+
vetoRateWarning = `WARNING: 40% veto rate over last 10 proposals — auto-pausing Freedom Run`;
|
|
2746
|
+
await freedomRun.pause(`Grillo veto rate ${(last10Vetoes / 10 * 100).toFixed(0)}% over 10-pitch window exceeds 40% threshold`, 'mighty-mark');
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
catch { /* veto rate check is best-effort */ }
|
|
2751
|
+
const suggestedActions = [];
|
|
2752
|
+
if (pipelineSummary.byStage.discovered > 0) {
|
|
2753
|
+
suggestedActions.push(`Qualify ${pipelineSummary.byStage.discovered} discovered prospect(s) — run nole_pipeline action=qualify`);
|
|
2754
|
+
}
|
|
2755
|
+
if (pipelineSummary.byStage.qualified > 0) {
|
|
2756
|
+
suggestedActions.push(`Pitch ${pipelineSummary.byStage.qualified} qualified prospect(s) — run nole_pipeline action=pitch`);
|
|
2757
|
+
}
|
|
2758
|
+
if (pipelineSummary.todayPitched < config.maxDailyPitches) {
|
|
2759
|
+
suggestedActions.push(`${config.maxDailyPitches - pipelineSummary.todayPitched} pitch slots remaining today`);
|
|
2760
|
+
}
|
|
2761
|
+
if (moltbook) {
|
|
2762
|
+
suggestedActions.push('Scan MoltBook for new prospects — run nole_pipeline action=scan');
|
|
2763
|
+
}
|
|
2764
|
+
if (window === 'morning') {
|
|
2765
|
+
return {
|
|
2766
|
+
content: [{
|
|
2767
|
+
type: "text",
|
|
2768
|
+
text: JSON.stringify({
|
|
2769
|
+
window: 'morning',
|
|
2770
|
+
date: today,
|
|
2771
|
+
freedomRun: freedomRun.getStatus().status,
|
|
2772
|
+
pipeline: pipelineSummary,
|
|
2773
|
+
wallet: walletBalance ? { balance: walletBalance, network: wallet.network } : { status: 'unavailable' },
|
|
2774
|
+
conversions: conversions.length > 0 ? conversions : 'none',
|
|
2775
|
+
vetoRateWarning,
|
|
2776
|
+
suggestedActions,
|
|
2777
|
+
}, null, 2),
|
|
2778
|
+
}],
|
|
2779
|
+
};
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
// ── Midday window: deliver approved pitches, post content ──
|
|
2783
|
+
if (window === 'midday') {
|
|
2784
|
+
let socialActivityToday = {};
|
|
2785
|
+
try {
|
|
2786
|
+
const interactions = await store.listSocialInteractions(undefined, 200);
|
|
2787
|
+
for (const i of interactions) {
|
|
2788
|
+
if (i.timestamp.startsWith(today)) {
|
|
2789
|
+
const key = `${i.platform}:${i.action}`;
|
|
2790
|
+
socialActivityToday[key] = (socialActivityToday[key] ?? 0) + 1;
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
catch {
|
|
2795
|
+
socialActivityToday = {};
|
|
2796
|
+
}
|
|
2797
|
+
const todayPlan = generateDailyContentPlan(new Date(), { maxDailyXPosts: config.maxDailyXPosts, moltbookScanFrequency: config.moltbookScanFrequency }, { activeSubscribers: pipelineSummary.byStage.onboarded + pipelineSummary.byStage.converted, weeklyConversions: pipelineSummary.todayConverted });
|
|
2798
|
+
return {
|
|
2799
|
+
content: [{
|
|
2800
|
+
type: "text",
|
|
2801
|
+
text: JSON.stringify({
|
|
2802
|
+
window: 'midday',
|
|
2803
|
+
date: today,
|
|
2804
|
+
freedomRun: freedomRun.getStatus().status,
|
|
2805
|
+
socialActivity: socialActivityToday,
|
|
2806
|
+
contentPlan: todayPlan,
|
|
2807
|
+
pitchSlots: Math.max(0, config.maxDailyPitches - pipelineSummary.todayPitched),
|
|
2808
|
+
suggestedActions: [
|
|
2809
|
+
'Deliver any approved pitches awaiting send',
|
|
2810
|
+
'Post thought leadership content per content plan',
|
|
2811
|
+
'Respond to inbound messages',
|
|
2812
|
+
],
|
|
2813
|
+
}, null, 2),
|
|
2814
|
+
}],
|
|
2815
|
+
};
|
|
2816
|
+
}
|
|
2817
|
+
// ── Afternoon window: nurture, commission sweep, content review ──
|
|
2818
|
+
if (window === 'afternoon') {
|
|
2819
|
+
const allProspects = await pipeline.listAll(200);
|
|
2820
|
+
const atRiskSubscribers = findAtRiskSubscribers(allProspects);
|
|
2821
|
+
let commissionStatus = null;
|
|
2822
|
+
try {
|
|
2823
|
+
const cs = await platform.getCommissionStatus();
|
|
2824
|
+
if (cs.ok)
|
|
2825
|
+
commissionStatus = cs.data;
|
|
2826
|
+
}
|
|
2827
|
+
catch { /* best-effort */ }
|
|
2828
|
+
// Commission sweep via governance — NOT direct claim (BB Amendment A4/A8)
|
|
2829
|
+
let commissionSweepResult = 'No eligible claims';
|
|
2830
|
+
let eligibleEscrows = 0;
|
|
2831
|
+
if (commissionStatus && typeof commissionStatus === 'object' && 'pendingClaims' in commissionStatus) {
|
|
2832
|
+
eligibleEscrows = commissionStatus.pendingClaims ?? 0;
|
|
2833
|
+
}
|
|
2834
|
+
if (eligibleEscrows > 0) {
|
|
2835
|
+
try {
|
|
2836
|
+
const escrows = commissionStatus?.eligibleEscrows ?? [{ escrowId: 'batch', amount: 0, subscriber: 'unknown' }];
|
|
2837
|
+
let proposalsSubmitted = 0;
|
|
2838
|
+
for (const escrow of escrows) {
|
|
2839
|
+
await governance.propose({
|
|
2840
|
+
actionType: 'financial',
|
|
2841
|
+
description: `Claim commission escrow #${escrow.escrowId} — $${escrow.amount} USDC from subscriber ${escrow.subscriber}`,
|
|
2842
|
+
riskLevel: 'critical',
|
|
2843
|
+
metadata: { escrowId: escrow.escrowId, amount: escrow.amount, subscriber: escrow.subscriber },
|
|
2844
|
+
});
|
|
2845
|
+
proposalsSubmitted++;
|
|
2846
|
+
}
|
|
2847
|
+
commissionSweepResult = `${proposalsSubmitted} commission claim proposal(s) submitted for Jessie's approval (risk: critical, NO auto-approval)`;
|
|
2848
|
+
}
|
|
2849
|
+
catch (err) {
|
|
2850
|
+
commissionSweepResult = `Commission sweep error: ${err instanceof Error ? err.message : String(err)}`;
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
return {
|
|
2854
|
+
content: [{
|
|
2855
|
+
type: "text",
|
|
2856
|
+
text: JSON.stringify({
|
|
2857
|
+
window: 'afternoon',
|
|
2858
|
+
date: today,
|
|
2859
|
+
freedomRun: freedomRun.getStatus().status,
|
|
2860
|
+
nurture: {
|
|
2861
|
+
atRiskCount: atRiskSubscribers.length,
|
|
2862
|
+
subscribers: atRiskSubscribers.length > 0 ? atRiskSubscribers : undefined,
|
|
2863
|
+
},
|
|
2864
|
+
commission: {
|
|
2865
|
+
status: commissionStatus,
|
|
2866
|
+
sweepResult: commissionSweepResult,
|
|
2867
|
+
note: 'All claims go through governance. Jessie must explicitly approve. No auto-approval timeout.',
|
|
2868
|
+
},
|
|
2869
|
+
suggestedActions: [
|
|
2870
|
+
...atRiskSubscribers.map(r => `[${r.status.toUpperCase()}] ${r.suggestedAction}`),
|
|
2871
|
+
'Review content performance metrics',
|
|
2872
|
+
],
|
|
2873
|
+
}, null, 2),
|
|
2874
|
+
}],
|
|
2875
|
+
};
|
|
2876
|
+
}
|
|
2877
|
+
// ── Evening window: daily report, metrics, report to Jessie ──
|
|
2878
|
+
if (window === 'evening') {
|
|
2879
|
+
const walletBalance = await wallet.getBalance().catch(() => null);
|
|
2880
|
+
const allProspects = await pipeline.listAll(200);
|
|
2881
|
+
const govStats = await governance.getStats();
|
|
2882
|
+
let socialActivityToday = {};
|
|
2883
|
+
try {
|
|
2884
|
+
const interactions = await store.listSocialInteractions(undefined, 200);
|
|
2885
|
+
for (const i of interactions) {
|
|
2886
|
+
if (i.timestamp.startsWith(today)) {
|
|
2887
|
+
const key = `${i.platform}:${i.action}`;
|
|
2888
|
+
socialActivityToday[key] = (socialActivityToday[key] ?? 0) + 1;
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
catch {
|
|
2893
|
+
socialActivityToday = {};
|
|
2894
|
+
}
|
|
2895
|
+
return {
|
|
2896
|
+
content: [{
|
|
2897
|
+
type: "text",
|
|
2898
|
+
text: JSON.stringify({
|
|
2899
|
+
window: 'evening',
|
|
2900
|
+
date: today,
|
|
2901
|
+
freedomRun: freedomRun.getStatus(),
|
|
2902
|
+
dailyReport: {
|
|
2903
|
+
pipeline: pipelineSummary,
|
|
2904
|
+
wallet: walletBalance ? { balance: walletBalance, network: wallet.network } : { status: 'unavailable' },
|
|
2905
|
+
governance: govStats,
|
|
2906
|
+
socialActivity: socialActivityToday,
|
|
2907
|
+
activeProspects: allProspects.filter(p => !['disqualified', 'declined'].includes(p.stage)).length,
|
|
2908
|
+
},
|
|
2909
|
+
suggestedActions: [
|
|
2910
|
+
'Submit daily report to Jessie (lands in her 17:30 CT afternoon review)',
|
|
2911
|
+
'Update pipeline metrics for weekly tracking',
|
|
2912
|
+
],
|
|
2913
|
+
}, null, 2),
|
|
2914
|
+
}],
|
|
2915
|
+
};
|
|
2916
|
+
}
|
|
2917
|
+
// ── Auto mode: full one-shot brief (backward compatible) ──
|
|
2651
2918
|
const walletBalance = await wallet.getBalance().catch(() => null);
|
|
2652
2919
|
let commissionStatus = null;
|
|
2653
2920
|
try {
|
|
@@ -2656,28 +2923,12 @@ export default function register(api) {
|
|
|
2656
2923
|
commissionStatus = cs.data;
|
|
2657
2924
|
}
|
|
2658
2925
|
catch { /* best-effort */ }
|
|
2659
|
-
|
|
2660
|
-
if (commissionStatus && typeof commissionStatus === 'object' && 'pendingClaims' in commissionStatus) {
|
|
2661
|
-
pendingClaims = commissionStatus.pendingClaims ?? 0;
|
|
2662
|
-
}
|
|
2663
|
-
let commissionClaimResult = null;
|
|
2664
|
-
if (pendingClaims > 0) {
|
|
2665
|
-
try {
|
|
2666
|
-
const claimed = await platform.claimCommission();
|
|
2667
|
-
commissionClaimResult = claimed.ok
|
|
2668
|
-
? `Claimed ${pendingClaims} pending commission(s)`
|
|
2669
|
-
: `Claim failed: ${claimed.error}`;
|
|
2670
|
-
}
|
|
2671
|
-
catch {
|
|
2672
|
-
commissionClaimResult = 'Claim attempt failed';
|
|
2673
|
-
}
|
|
2674
|
-
}
|
|
2675
|
-
const today = new Date().toISOString().slice(0, 10);
|
|
2926
|
+
const today2 = today;
|
|
2676
2927
|
let socialActivityToday = {};
|
|
2677
2928
|
try {
|
|
2678
2929
|
const interactions = await store.listSocialInteractions(undefined, 200);
|
|
2679
2930
|
for (const i of interactions) {
|
|
2680
|
-
if (i.timestamp.startsWith(
|
|
2931
|
+
if (i.timestamp.startsWith(today2)) {
|
|
2681
2932
|
const key = `${i.platform}:${i.action}`;
|
|
2682
2933
|
socialActivityToday[key] = (socialActivityToday[key] ?? 0) + 1;
|
|
2683
2934
|
}
|
|
@@ -2707,15 +2958,14 @@ export default function register(api) {
|
|
|
2707
2958
|
}
|
|
2708
2959
|
}
|
|
2709
2960
|
const brief = {
|
|
2961
|
+
window: 'auto',
|
|
2710
2962
|
date: today,
|
|
2963
|
+
freedomRun: freedomRun.getStatus().status,
|
|
2711
2964
|
pipeline: pipelineSummary,
|
|
2712
2965
|
wallet: walletBalance
|
|
2713
2966
|
? { balance: walletBalance, network: wallet.network }
|
|
2714
2967
|
: { status: 'unavailable' },
|
|
2715
|
-
commission: {
|
|
2716
|
-
status: commissionStatus,
|
|
2717
|
-
claimResult: commissionClaimResult,
|
|
2718
|
-
},
|
|
2968
|
+
commission: { status: commissionStatus, note: 'Use nole_commission_sweep for governance-gated claims' },
|
|
2719
2969
|
socialActivity: socialActivityToday,
|
|
2720
2970
|
suggestedActions,
|
|
2721
2971
|
nurture: {
|
|
@@ -2728,10 +2978,7 @@ export default function register(api) {
|
|
|
2728
2978
|
socialPostingMode: config.socialPostingMode,
|
|
2729
2979
|
},
|
|
2730
2980
|
};
|
|
2731
|
-
const todayPlan = generateDailyContentPlan(new Date(), { maxDailyXPosts: config.maxDailyXPosts, moltbookScanFrequency: config.moltbookScanFrequency }, {
|
|
2732
|
-
activeSubscribers: pipelineSummary.byStage.onboarded + pipelineSummary.byStage.converted,
|
|
2733
|
-
weeklyConversions: pipelineSummary.todayConverted,
|
|
2734
|
-
});
|
|
2981
|
+
const todayPlan = generateDailyContentPlan(new Date(), { maxDailyXPosts: config.maxDailyXPosts, moltbookScanFrequency: config.moltbookScanFrequency }, { activeSubscribers: pipelineSummary.byStage.onboarded + pipelineSummary.byStage.converted, weeklyConversions: pipelineSummary.todayConverted });
|
|
2735
2982
|
brief.contentPlan = todayPlan;
|
|
2736
2983
|
if (params.includeProspects) {
|
|
2737
2984
|
const active = await pipeline.listAll(50);
|
|
@@ -2745,6 +2992,81 @@ export default function register(api) {
|
|
|
2745
2992
|
}
|
|
2746
2993
|
},
|
|
2747
2994
|
});
|
|
2995
|
+
// ── Commission Sweep Tool (BB Amendment A4/A8: governance-gated, no auto-approval) ──
|
|
2996
|
+
api.registerTool({
|
|
2997
|
+
name: "nole_commission_sweep",
|
|
2998
|
+
description: "Sweep eligible commission escrows and submit governance proposals for each claim. " +
|
|
2999
|
+
"Every claim requires Jessie's explicit approval (risk: critical, NO auto-approval timeout). " +
|
|
3000
|
+
"BB Amendment A8: Patent 8 separation of powers.",
|
|
3001
|
+
parameters: {
|
|
3002
|
+
type: "object",
|
|
3003
|
+
properties: {},
|
|
3004
|
+
},
|
|
3005
|
+
async execute(_toolCallId, _params) {
|
|
3006
|
+
if (!freedomRun.canOperate()) {
|
|
3007
|
+
const frStatus = freedomRun.getStatus();
|
|
3008
|
+
return {
|
|
3009
|
+
content: [{
|
|
3010
|
+
type: "text",
|
|
3011
|
+
text: JSON.stringify({
|
|
3012
|
+
tool: 'nole_commission_sweep',
|
|
3013
|
+
blocked: true,
|
|
3014
|
+
freedomRunStatus: frStatus.status,
|
|
3015
|
+
reason: frStatus.reason,
|
|
3016
|
+
}, null, 2),
|
|
3017
|
+
}],
|
|
3018
|
+
};
|
|
3019
|
+
}
|
|
3020
|
+
try {
|
|
3021
|
+
let commissionStatus = null;
|
|
3022
|
+
try {
|
|
3023
|
+
const cs = await platform.getCommissionStatus();
|
|
3024
|
+
if (cs.ok)
|
|
3025
|
+
commissionStatus = cs.data;
|
|
3026
|
+
}
|
|
3027
|
+
catch { /* best-effort */ }
|
|
3028
|
+
let eligibleEscrows = 0;
|
|
3029
|
+
if (commissionStatus && typeof commissionStatus === 'object' && 'pendingClaims' in commissionStatus) {
|
|
3030
|
+
eligibleEscrows = commissionStatus.pendingClaims ?? 0;
|
|
3031
|
+
}
|
|
3032
|
+
if (eligibleEscrows === 0) {
|
|
3033
|
+
return {
|
|
3034
|
+
content: [{
|
|
3035
|
+
type: "text",
|
|
3036
|
+
text: JSON.stringify({ result: 'No eligible commission escrows to claim', eligibleEscrows: 0 }, null, 2),
|
|
3037
|
+
}],
|
|
3038
|
+
};
|
|
3039
|
+
}
|
|
3040
|
+
const escrows = commissionStatus?.eligibleEscrows ?? [{ escrowId: 'batch', amount: 0, subscriber: 'unknown' }];
|
|
3041
|
+
const proposals = [];
|
|
3042
|
+
for (const escrow of escrows) {
|
|
3043
|
+
const result = await governance.propose({
|
|
3044
|
+
actionType: 'financial',
|
|
3045
|
+
description: `Claim commission escrow #${escrow.escrowId} — $${escrow.amount} USDC from subscriber ${escrow.subscriber}`,
|
|
3046
|
+
riskLevel: 'critical',
|
|
3047
|
+
metadata: { escrowId: escrow.escrowId, amount: escrow.amount, subscriber: escrow.subscriber },
|
|
3048
|
+
});
|
|
3049
|
+
proposals.push(result.proposalId);
|
|
3050
|
+
}
|
|
3051
|
+
return {
|
|
3052
|
+
content: [{
|
|
3053
|
+
type: "text",
|
|
3054
|
+
text: JSON.stringify({
|
|
3055
|
+
result: `${proposals.length} commission claim proposal(s) submitted`,
|
|
3056
|
+
proposals,
|
|
3057
|
+
riskTier: 'critical',
|
|
3058
|
+
autoApproval: 'NEVER — Jessie must explicitly approve every financial action (BB Amendment A8)',
|
|
3059
|
+
note: 'Escrows do not expire. Claims will wait for Jessie\'s batch review.',
|
|
3060
|
+
}, null, 2),
|
|
3061
|
+
}],
|
|
3062
|
+
};
|
|
3063
|
+
}
|
|
3064
|
+
catch (err) {
|
|
3065
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3066
|
+
return { content: [{ type: "text", text: `Commission sweep error: ${message}` }], isError: true };
|
|
3067
|
+
}
|
|
3068
|
+
},
|
|
3069
|
+
});
|
|
2748
3070
|
// --- Command (matches Grillo/NOAH pattern: sync handler, ctx.args) ---
|
|
2749
3071
|
api.registerCommand({
|
|
2750
3072
|
name: "nole",
|
|
@@ -2777,7 +3099,8 @@ export default function register(api) {
|
|
|
2777
3099
|
"- `nole_setup` — Configuration check\n\n" +
|
|
2778
3100
|
"**Revenue Pipeline:**\n" +
|
|
2779
3101
|
"- `nole_pipeline` — Manage prospect pipeline (discover, qualify, pitch, scan, onboard, promote, nurture, directory-scan, content-plan, list, summary)\n" +
|
|
2780
|
-
"- `nole_daily_brief` —
|
|
3102
|
+
"- `nole_daily_brief` — Windowed daily brief (morning/midday/afternoon/evening/auto) with Freedom Run gate\n" +
|
|
3103
|
+
"- `nole_commission_sweep` — Governance-gated commission claims (requires Jessie approval)\n\n" +
|
|
2781
3104
|
"**Social Media:**\n" +
|
|
2782
3105
|
"- `nole_moltbook_post` — Post to a MoltBook submolt\n" +
|
|
2783
3106
|
"- `nole_moltbook_comment` — Comment on a MoltBook post\n" +
|
|
@@ -2923,6 +3246,42 @@ export default function register(api) {
|
|
|
2923
3246
|
}
|
|
2924
3247
|
if (msg.method === 'cron/scheduled') {
|
|
2925
3248
|
const { action, jobId } = msg.params ?? {};
|
|
3249
|
+
// Freedom Run daily windows (§11.4 — 4 cron entries aligned with Jessie's EA schedule)
|
|
3250
|
+
const cronWindowMap = {
|
|
3251
|
+
nole_morning_scan: 'morning',
|
|
3252
|
+
nole_midday_outreach: 'midday',
|
|
3253
|
+
nole_afternoon_nurture: 'afternoon',
|
|
3254
|
+
nole_evening_report: 'evening',
|
|
3255
|
+
};
|
|
3256
|
+
if (action in cronWindowMap) {
|
|
3257
|
+
const window = cronWindowMap[action];
|
|
3258
|
+
console.log(`[nole] Cron-triggered ${window} window (job: ${jobId})`);
|
|
3259
|
+
try {
|
|
3260
|
+
await transport.callGateway({
|
|
3261
|
+
method: 'agent',
|
|
3262
|
+
params: {
|
|
3263
|
+
agentId: 'nole',
|
|
3264
|
+
message: `SYSTEM: Freedom Run ${window} window — cron-triggered by Noah. Execute nole_daily_brief with window="${window}".`,
|
|
3265
|
+
deliver: true,
|
|
3266
|
+
label: `cron:${action}`,
|
|
3267
|
+
},
|
|
3268
|
+
});
|
|
3269
|
+
await fleetSend(bus, transport, {
|
|
3270
|
+
to: 'noah',
|
|
3271
|
+
method: 'cron/completed',
|
|
3272
|
+
params: { jobId, action, status: 'completed', timestamp: new Date().toISOString() },
|
|
3273
|
+
}).catch(() => { });
|
|
3274
|
+
}
|
|
3275
|
+
catch (err) {
|
|
3276
|
+
console.error(`[nole] Cron injection failed for ${action}:`, err instanceof Error ? err.message : String(err));
|
|
3277
|
+
await fleetSend(bus, transport, {
|
|
3278
|
+
to: 'noah',
|
|
3279
|
+
method: 'cron/failed',
|
|
3280
|
+
params: { jobId, action, error: err instanceof Error ? err.message : String(err), timestamp: new Date().toISOString() },
|
|
3281
|
+
}).catch(() => { });
|
|
3282
|
+
}
|
|
3283
|
+
return;
|
|
3284
|
+
}
|
|
2926
3285
|
if (action === 'weekly_strategy_review') {
|
|
2927
3286
|
console.log(`[nole] Cron-triggered weekly review (job: ${jobId})`);
|
|
2928
3287
|
try {
|
|
@@ -2955,6 +3314,65 @@ export default function register(api) {
|
|
|
2955
3314
|
}
|
|
2956
3315
|
return;
|
|
2957
3316
|
}
|
|
3317
|
+
// ── Pitch delivery on governance approval (§11.5) ──
|
|
3318
|
+
if (msg.method === 'proposal/approved') {
|
|
3319
|
+
const { actionType, metadata, description } = msg.params ?? {};
|
|
3320
|
+
if (actionType === 'recruitment' || actionType === 'social' || actionType === 'content') {
|
|
3321
|
+
console.log(`[nole] Pitch approved — delivering via social client`);
|
|
3322
|
+
if (!freedomRun.canOperate()) {
|
|
3323
|
+
console.warn('[nole] Pitch delivery skipped — Freedom Run not active');
|
|
3324
|
+
}
|
|
3325
|
+
else {
|
|
3326
|
+
try {
|
|
3327
|
+
const targetPlatform = metadata?.channel ?? metadata?.platform ?? 'moltbook';
|
|
3328
|
+
const pitchContent = metadata?.pitchContent ?? description ?? '';
|
|
3329
|
+
const prospectId = metadata?.prospectId;
|
|
3330
|
+
if (targetPlatform === 'moltbook' && moltbook) {
|
|
3331
|
+
await moltbook.createPost('aiassesstech', 'Nole Outreach', String(pitchContent));
|
|
3332
|
+
}
|
|
3333
|
+
else if (targetPlatform === 'x' && xClient) {
|
|
3334
|
+
await xClient.postTweet(String(pitchContent));
|
|
3335
|
+
}
|
|
3336
|
+
// fleet-bus pitches go via fleetSend in the original proposal handler
|
|
3337
|
+
if (prospectId) {
|
|
3338
|
+
await pipeline.updateStage(String(prospectId), 'pitched', {
|
|
3339
|
+
pitchDeliveredAt: new Date().toISOString(),
|
|
3340
|
+
pitchApproved: true,
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3343
|
+
console.log(`[nole] Pitch delivered to ${targetPlatform}`);
|
|
3344
|
+
}
|
|
3345
|
+
catch (err) {
|
|
3346
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3347
|
+
console.error(`[nole] Pitch delivery failed: ${errMsg}`);
|
|
3348
|
+
// Queue for retry via resilience layer
|
|
3349
|
+
if (metadata?.pitchContent) {
|
|
3350
|
+
postQueue.enqueue(metadata.platform ?? 'moltbook', String(metadata.pitchContent), metadata);
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
// Commission claim on financial approval (§11.6 — BB Amendment A4/A8)
|
|
3356
|
+
if (actionType === 'financial') {
|
|
3357
|
+
const escrowId = metadata?.escrowId;
|
|
3358
|
+
console.log(`[nole] Financial action approved — claiming commission escrow ${escrowId}`);
|
|
3359
|
+
try {
|
|
3360
|
+
const result = await platform.claimCommission();
|
|
3361
|
+
if (result.ok) {
|
|
3362
|
+
console.log(`[nole] Commission claimed successfully (escrow: ${escrowId})`);
|
|
3363
|
+
}
|
|
3364
|
+
else {
|
|
3365
|
+
console.error(`[nole] Commission claim failed: ${result.error}`);
|
|
3366
|
+
}
|
|
3367
|
+
await governance.auditTrail.record('commission_claimed', `Commission escrow ${escrowId}: ${result.ok ? 'claimed' : `failed — ${result.error}`}`);
|
|
3368
|
+
}
|
|
3369
|
+
catch (err) {
|
|
3370
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
3371
|
+
console.error(`[nole] Commission claim error: ${errMsg}`);
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
// Don't return — let the existing fleet-discovery logic below also run
|
|
3375
|
+
}
|
|
2958
3376
|
if (msg.from && msg.from !== 'nole') {
|
|
2959
3377
|
const existing = await pipeline.findByAgentName(msg.from);
|
|
2960
3378
|
if (!existing) {
|
|
@@ -3036,7 +3454,7 @@ export default function register(api) {
|
|
|
3036
3454
|
`nole_directory, nole_review_pending, nole_approve, nole_veto, nole_fleet_overview, ` +
|
|
3037
3455
|
`nole_moltbook_post, nole_moltbook_comment, nole_moltbook_feed, nole_moltbook_search, ` +
|
|
3038
3456
|
`nole_x_post, nole_x_thread, nole_linkedin_draft, nole_social_status, ` +
|
|
3039
|
-
`nole_pipeline, nole_daily_brief tools; /nole command`);
|
|
3457
|
+
`nole_pipeline, nole_daily_brief, nole_commission_sweep tools; /nole command`);
|
|
3040
3458
|
console.log(`[nole] AIAssessTech API: ${platform.isConfigured ? 'configured' : 'not configured — set agentApiKey + platformWalletAddress'} (${config.platformApiUrl})`);
|
|
3041
3459
|
}
|
|
3042
3460
|
//# sourceMappingURL=plugin.js.map
|