@fml-inc/fml 0.8.9 → 0.8.10
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/.claude-plugin/plugin.json +1 -1
- package/README.md +114 -3
- package/agent-assets/mini-swe-agent/FML_WORKFLOW.md +62 -0
- package/agent-assets/mini-swe-agent/fml_mini_agent.py +483 -0
- package/agent-assets/mini-swe-agent/fml_mini_bootstrap.py +141 -0
- package/bin/fml +18 -4
- package/dist/{chunk-2DGHAVVR.js → chunk-OUXFHL3I.js} +1 -1
- package/dist/{chunk-JPHN4VKM.js → chunk-PTDADW47.js} +197 -94
- package/dist/{chunk-TWJ62CZX.js → chunk-U4BM27VN.js} +1 -1
- package/dist/{chunk-H5LWJ5PH.js → chunk-XB7EIDWY.js} +123 -67
- package/dist/cli.js +4608 -3883
- package/dist/hook-handler.js +39 -11
- package/dist/mcp/server.js +5 -5
- package/dist/observer-server.js +51 -11
- package/dist/pi/extension.js +17 -4
- package/dist/sync-cycle.js +2 -2
- package/package.json +2 -2
|
@@ -53,7 +53,7 @@ var PACKAGE_NAME = "@fml-inc/fml";
|
|
|
53
53
|
var UPDATE_TIMEOUT_MS = 12e4;
|
|
54
54
|
var SETUP_TIMEOUT_MS = 3e5;
|
|
55
55
|
function getCurrentVersion() {
|
|
56
|
-
return true ? "0.8.
|
|
56
|
+
return true ? "0.8.10+45913c27" : "unknown";
|
|
57
57
|
}
|
|
58
58
|
function parseVersion(version) {
|
|
59
59
|
const match = SEMVER_PATTERN.exec(version);
|
|
@@ -75,7 +75,8 @@ var HOOK_TARGETS = Object.freeze([
|
|
|
75
75
|
"codex",
|
|
76
76
|
"gemini",
|
|
77
77
|
"grok",
|
|
78
|
-
"pi"
|
|
78
|
+
"pi",
|
|
79
|
+
"mini-swe-agent"
|
|
79
80
|
]);
|
|
80
81
|
function isHookTarget(value) {
|
|
81
82
|
return typeof value === "string" && HOOK_TARGETS.some((targetId) => targetId === value);
|
|
@@ -155,6 +156,14 @@ var hook_events_default = {
|
|
|
155
156
|
"PreCompact",
|
|
156
157
|
"PostCompact",
|
|
157
158
|
"SessionEnd"
|
|
159
|
+
],
|
|
160
|
+
"mini-swe-agent": [
|
|
161
|
+
"SessionStart",
|
|
162
|
+
"UserPromptSubmit",
|
|
163
|
+
"BeforeModel",
|
|
164
|
+
"PostToolUse",
|
|
165
|
+
"Stop",
|
|
166
|
+
"SessionEnd"
|
|
158
167
|
]
|
|
159
168
|
};
|
|
160
169
|
|
|
@@ -172,6 +181,9 @@ var GROK_HOOK_EVENTS = Object.freeze([
|
|
|
172
181
|
...hook_events_default.grok
|
|
173
182
|
]);
|
|
174
183
|
var PI_HOOK_EVENTS = Object.freeze([...hook_events_default.pi]);
|
|
184
|
+
var MINI_SWE_AGENT_HOOK_EVENTS = Object.freeze([
|
|
185
|
+
...hook_events_default["mini-swe-agent"]
|
|
186
|
+
]);
|
|
175
187
|
|
|
176
188
|
// ../../packages/agent-targets/src/hooks/read-command.ts
|
|
177
189
|
import path2 from "path";
|
|
@@ -547,9 +559,9 @@ function normalizedEvent(target, payload, context) {
|
|
|
547
559
|
"command"
|
|
548
560
|
);
|
|
549
561
|
const toolName = firstString(payload, ["tool_name", "toolName", "name"]);
|
|
550
|
-
const filePath = firstString(payload, ["file_path", "filePath", "path"]) ?? (toolInput ? firstString(toolInput, ["file_path", "filePath", "path"]) : void 0) ?? (command && toolName && SHELL_TOOL_NAMES.has(toolName.toLowerCase()) ? extractReadFilePathFromCommand(command) ?? void 0 : void 0);
|
|
562
|
+
const filePath = firstString(payload, ["file_path", "filePath", "path"]) ?? (toolInput ? firstString(toolInput, ["file_path", "filePath", "path"]) : void 0) ?? (target !== "mini-swe-agent" && command && toolName && SHELL_TOOL_NAMES.has(toolName.toLowerCase()) ? extractReadFilePathFromCommand(command) ?? void 0 : void 0);
|
|
551
563
|
const userPrompt = boundedExtractedText(
|
|
552
|
-
firstText(payload, ["prompt", "user_prompt", "userPrompt"]) ?? (target === "gemini" ? geminiUserPrompt(payload) : void 0),
|
|
564
|
+
target === "mini-swe-agent" && eventType !== "UserPromptSubmit" ? void 0 : firstText(payload, ["prompt", "user_prompt", "userPrompt"]) ?? (target === "gemini" ? geminiUserPrompt(payload) : void 0),
|
|
553
565
|
"user_prompt"
|
|
554
566
|
);
|
|
555
567
|
const plan = boundedExtractedText(firstText(payload, ["plan"]), "plan");
|
|
@@ -605,7 +617,8 @@ var HOOK_NORMALIZATION_CAPABILITIES = Object.freeze([
|
|
|
605
617
|
capability("codex", CODEX_HOOK_EVENTS),
|
|
606
618
|
capability("gemini", GEMINI_HOOK_EVENTS),
|
|
607
619
|
capability("grok", GROK_HOOK_EVENTS),
|
|
608
|
-
capability("pi", PI_HOOK_EVENTS)
|
|
620
|
+
capability("pi", PI_HOOK_EVENTS),
|
|
621
|
+
capability("mini-swe-agent", MINI_SWE_AGENT_HOOK_EVENTS)
|
|
609
622
|
]);
|
|
610
623
|
function parseHookCaptureRequest(value) {
|
|
611
624
|
if (!isRecord(value) || !isHookTarget(value.target)) return null;
|
|
@@ -695,6 +708,7 @@ var MAX_HOOK_DESTINATION_IDENTITY_BYTES = 4 * 1024;
|
|
|
695
708
|
var DEFAULT_OTLP_OUTBOX_BATCH_SIZE = 100;
|
|
696
709
|
var MAX_OTLP_OUTBOX_BATCH_SIZE = 100;
|
|
697
710
|
var MAX_OTLP_ENVELOPE_BYTES = 16 * 1024 * 1024;
|
|
711
|
+
var DEFAULT_OTLP_SPOOL_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
698
712
|
var DEFAULT_OTLP_SPOOL_MAX_BYTES = 1024 * 1024 * 1024;
|
|
699
713
|
var MAX_OTLP_ENVELOPE_ID_BYTES = 1024;
|
|
700
714
|
var MAX_OTLP_OBSERVER_INSTANCE_ID_BYTES = 4 * 1024;
|
|
@@ -703,8 +717,6 @@ var MAX_OTLP_DROP_REASON_BYTES = 4 * 1024;
|
|
|
703
717
|
var MAX_OTLP_DESTINATION_ORGANIZATION_ID_BYTES = 128;
|
|
704
718
|
var MAX_OTLP_DESTINATION_ORGANIZATION_SLUG_BYTES = 4 * 1024;
|
|
705
719
|
var OWNERLESS_OTLP_DROP_REASON = "ownerless";
|
|
706
|
-
var OTLP_CAPACITY_DROP_REASON = "spool_capacity";
|
|
707
|
-
var OTLP_ENTRY_TOO_LARGE_DROP_REASON = "spool_entry_too_large";
|
|
708
720
|
var OTLP_ROW_FIXED_LOGICAL_BYTES = 256;
|
|
709
721
|
var MAX_TIMESTAMP_MS = 864e13;
|
|
710
722
|
var QUARANTINE_PRUNE_INTERVAL_MS = 5 * 60 * 1e3;
|
|
@@ -1002,15 +1014,23 @@ function enforceOwnerOnlyMode(filePath, mode) {
|
|
|
1002
1014
|
function openObserverDatabase(databaseFile, options = {}) {
|
|
1003
1015
|
const maxHookSpoolBytes = options.maxHookSpoolBytes;
|
|
1004
1016
|
const maxOtlpSpoolBytes = options.maxOtlpSpoolBytes ?? DEFAULT_OTLP_SPOOL_MAX_BYTES;
|
|
1005
|
-
|
|
1006
|
-
if (maxHookSpoolBytes !== void 0 && (!Number.isSafeInteger(maxHookSpoolBytes) || maxHookSpoolBytes < 1)) {
|
|
1017
|
+
if (!Number.isSafeInteger(maxOtlpSpoolBytes) || maxOtlpSpoolBytes < 1) {
|
|
1007
1018
|
throw new Error(
|
|
1008
|
-
"
|
|
1019
|
+
"OTLP spool maximum must be a positive safe integer number of bytes"
|
|
1009
1020
|
);
|
|
1010
1021
|
}
|
|
1011
|
-
|
|
1022
|
+
const otlpLowWaterBytes = Math.floor(maxOtlpSpoolBytes * 0.9);
|
|
1023
|
+
let otlpCapacityCleanupActive = false;
|
|
1024
|
+
const maxOtlpSpoolAgeMs = options.maxOtlpSpoolAgeMs ?? DEFAULT_OTLP_SPOOL_MAX_AGE_MS;
|
|
1025
|
+
if (!Number.isSafeInteger(maxOtlpSpoolAgeMs) || maxOtlpSpoolAgeMs < 1) {
|
|
1012
1026
|
throw new Error(
|
|
1013
|
-
"OTLP spool maximum must be a positive safe integer number of
|
|
1027
|
+
"OTLP spool maximum age must be a positive safe integer number of milliseconds"
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
const maxHookQuarantineAgeMs = options.maxHookQuarantineAgeMs;
|
|
1031
|
+
if (maxHookSpoolBytes !== void 0 && (!Number.isSafeInteger(maxHookSpoolBytes) || maxHookSpoolBytes < 1)) {
|
|
1032
|
+
throw new Error(
|
|
1033
|
+
"Hook spool maximum must be a positive safe integer number of bytes"
|
|
1014
1034
|
);
|
|
1015
1035
|
}
|
|
1016
1036
|
if (maxHookQuarantineAgeMs !== void 0 && (!Number.isSafeInteger(maxHookQuarantineAgeMs) || maxHookQuarantineAgeMs < 1)) {
|
|
@@ -2031,27 +2051,24 @@ function openObserverDatabase(databaseFile, options = {}) {
|
|
|
2031
2051
|
last_drop_reason = ?
|
|
2032
2052
|
WHERE singleton = 1
|
|
2033
2053
|
`);
|
|
2034
|
-
const
|
|
2035
|
-
|
|
2036
|
-
SELECT
|
|
2037
|
-
logical_bytes,
|
|
2038
|
-
length(body) AS body_bytes,
|
|
2039
|
-
SUM(logical_bytes) OVER (ORDER BY id ASC) - logical_bytes
|
|
2040
|
-
AS preceding_bytes
|
|
2041
|
-
FROM otlp_envelope_outbox
|
|
2042
|
-
)
|
|
2043
|
-
SELECT
|
|
2044
|
-
COUNT(*) AS record_count,
|
|
2045
|
-
COALESCE(SUM(logical_bytes), 0) AS logical_bytes,
|
|
2046
|
-
COALESCE(SUM(body_bytes), 0) AS body_bytes
|
|
2047
|
-
FROM ordered
|
|
2048
|
-
WHERE preceding_bytes < ?
|
|
2054
|
+
const hasExpiredOtlpEnvelopes = database.prepare(`
|
|
2055
|
+
SELECT 1 FROM otlp_envelope_outbox WHERE captured_at_ms < ? LIMIT 1
|
|
2049
2056
|
`);
|
|
2050
|
-
const
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2057
|
+
const expiredOtlpEnvelopes = database.prepare(`
|
|
2058
|
+
SELECT id, logical_bytes, length(body) AS body_bytes
|
|
2059
|
+
FROM otlp_envelope_outbox
|
|
2060
|
+
WHERE captured_at_ms < ?
|
|
2061
|
+
ORDER BY captured_at_ms, id
|
|
2062
|
+
LIMIT 100
|
|
2063
|
+
`);
|
|
2064
|
+
const oldestOtlpEnvelopes = database.prepare(`
|
|
2065
|
+
SELECT id, logical_bytes, length(body) AS body_bytes
|
|
2066
|
+
FROM otlp_envelope_outbox
|
|
2067
|
+
ORDER BY captured_at_ms, id
|
|
2068
|
+
LIMIT 100
|
|
2069
|
+
`);
|
|
2070
|
+
const deleteExpiredOtlpEnvelope = database.prepare(`
|
|
2071
|
+
DELETE FROM otlp_envelope_outbox WHERE id = ?
|
|
2055
2072
|
`);
|
|
2056
2073
|
const getDestinationPendingOtlpEnvelopeAccounting = database.prepare(`
|
|
2057
2074
|
SELECT logical_bytes, length(body) AS body_bytes
|
|
@@ -2652,42 +2669,6 @@ function openObserverDatabase(databaseFile, options = {}) {
|
|
|
2652
2669
|
);
|
|
2653
2670
|
return null;
|
|
2654
2671
|
}
|
|
2655
|
-
if (logicalBytes > maxOtlpSpoolBytes) {
|
|
2656
|
-
recordOtlpDrop(
|
|
2657
|
-
1,
|
|
2658
|
-
logicalBytes,
|
|
2659
|
-
envelope.body.byteLength,
|
|
2660
|
-
capturedAtMs,
|
|
2661
|
-
OTLP_ENTRY_TOO_LARGE_DROP_REASON
|
|
2662
|
-
);
|
|
2663
|
-
return null;
|
|
2664
|
-
}
|
|
2665
|
-
const retainedBytes = requireSpoolInteger(
|
|
2666
|
-
getOtlpSpoolState.get(),
|
|
2667
|
-
"logical_bytes"
|
|
2668
|
-
);
|
|
2669
|
-
const bytesToFree = retainedBytes + logicalBytes - maxOtlpSpoolBytes;
|
|
2670
|
-
if (bytesToFree > 0) {
|
|
2671
|
-
const plan = otlpCapacityEvictionPlan.get(bytesToFree);
|
|
2672
|
-
const evictedRecords = requireSpoolInteger(plan, "record_count");
|
|
2673
|
-
const evictedLogicalBytes = requireSpoolInteger(
|
|
2674
|
-
plan,
|
|
2675
|
-
"logical_bytes"
|
|
2676
|
-
);
|
|
2677
|
-
const evictedBodyBytes = requireSpoolInteger(plan, "body_bytes");
|
|
2678
|
-
if (evictedRecords === 0 || evictedLogicalBytes < bytesToFree || evictOldestPendingOtlpEnvelopes.run(evictedRecords).changes !== evictedRecords) {
|
|
2679
|
-
throw new Error(
|
|
2680
|
-
"FML observer OTLP spool accounting could not enforce its limit"
|
|
2681
|
-
);
|
|
2682
|
-
}
|
|
2683
|
-
recordOtlpDrop(
|
|
2684
|
-
evictedRecords,
|
|
2685
|
-
evictedLogicalBytes,
|
|
2686
|
-
evictedBodyBytes,
|
|
2687
|
-
capturedAtMs,
|
|
2688
|
-
OTLP_CAPACITY_DROP_REASON
|
|
2689
|
-
);
|
|
2690
|
-
}
|
|
2691
2672
|
captureOtlpEnvelope.run(
|
|
2692
2673
|
envelopeId,
|
|
2693
2674
|
envelope.observerInstanceId,
|
|
@@ -2885,6 +2866,80 @@ function openObserverDatabase(databaseFile, options = {}) {
|
|
|
2885
2866
|
return droppedRecords;
|
|
2886
2867
|
});
|
|
2887
2868
|
},
|
|
2869
|
+
pruneExpiredOtlpEnvelopes: (nowMs = Date.now()) => {
|
|
2870
|
+
requireTimestamp(nowMs, "OTLP retention timestamp");
|
|
2871
|
+
const cutoffMs = nowMs - maxOtlpSpoolAgeMs;
|
|
2872
|
+
if (!hasExpiredOtlpEnvelopes.get(cutoffMs)) return 0;
|
|
2873
|
+
return runImmediateTransaction(() => {
|
|
2874
|
+
const rows = expiredOtlpEnvelopes.all(cutoffMs);
|
|
2875
|
+
let logicalBytes = 0;
|
|
2876
|
+
let bodyBytes = 0;
|
|
2877
|
+
for (const row of rows) {
|
|
2878
|
+
logicalBytes += requireSpoolInteger(row, "logical_bytes");
|
|
2879
|
+
bodyBytes += requireSpoolInteger(row, "body_bytes");
|
|
2880
|
+
if (deleteExpiredOtlpEnvelope.run(row.id).changes !== 1) {
|
|
2881
|
+
throw new Error(
|
|
2882
|
+
"FML observer OTLP retention deletion was incomplete"
|
|
2883
|
+
);
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
recordOtlpDrop(
|
|
2887
|
+
rows.length,
|
|
2888
|
+
logicalBytes,
|
|
2889
|
+
bodyBytes,
|
|
2890
|
+
nowMs,
|
|
2891
|
+
"spool_expired"
|
|
2892
|
+
);
|
|
2893
|
+
return rows.length;
|
|
2894
|
+
});
|
|
2895
|
+
},
|
|
2896
|
+
pruneOtlpCapacity: (nowMs = Date.now()) => {
|
|
2897
|
+
requireTimestamp(nowMs, "OTLP capacity cleanup timestamp");
|
|
2898
|
+
const retainedBytes = requireSpoolInteger(
|
|
2899
|
+
getOtlpSpoolState.get(),
|
|
2900
|
+
"logical_bytes"
|
|
2901
|
+
);
|
|
2902
|
+
if (retainedBytes <= otlpLowWaterBytes)
|
|
2903
|
+
otlpCapacityCleanupActive = false;
|
|
2904
|
+
if (retainedBytes > maxOtlpSpoolBytes) otlpCapacityCleanupActive = true;
|
|
2905
|
+
if (!otlpCapacityCleanupActive) return 0;
|
|
2906
|
+
return runImmediateTransaction(() => {
|
|
2907
|
+
const currentBytes = requireSpoolInteger(
|
|
2908
|
+
getOtlpSpoolState.get(),
|
|
2909
|
+
"logical_bytes"
|
|
2910
|
+
);
|
|
2911
|
+
if (currentBytes <= otlpLowWaterBytes) {
|
|
2912
|
+
otlpCapacityCleanupActive = false;
|
|
2913
|
+
return 0;
|
|
2914
|
+
}
|
|
2915
|
+
let records = 0;
|
|
2916
|
+
let logicalBytes = 0;
|
|
2917
|
+
let bodyBytes = 0;
|
|
2918
|
+
for (const row of oldestOtlpEnvelopes.all()) {
|
|
2919
|
+
logicalBytes += requireSpoolInteger(row, "logical_bytes");
|
|
2920
|
+
bodyBytes += requireSpoolInteger(row, "body_bytes");
|
|
2921
|
+
if (deleteExpiredOtlpEnvelope.run(row.id).changes !== 1) {
|
|
2922
|
+
throw new Error(
|
|
2923
|
+
"FML observer OTLP capacity deletion was incomplete"
|
|
2924
|
+
);
|
|
2925
|
+
}
|
|
2926
|
+
records += 1;
|
|
2927
|
+
if (currentBytes - logicalBytes <= otlpLowWaterBytes) break;
|
|
2928
|
+
}
|
|
2929
|
+
if (records === 0)
|
|
2930
|
+
throw new Error(
|
|
2931
|
+
"FML observer OTLP capacity accounting is inconsistent"
|
|
2932
|
+
);
|
|
2933
|
+
recordOtlpDrop(
|
|
2934
|
+
records,
|
|
2935
|
+
logicalBytes,
|
|
2936
|
+
bodyBytes,
|
|
2937
|
+
nowMs,
|
|
2938
|
+
"spool_capacity"
|
|
2939
|
+
);
|
|
2940
|
+
return records;
|
|
2941
|
+
});
|
|
2942
|
+
},
|
|
2888
2943
|
getOtlpSpoolStatus: () => {
|
|
2889
2944
|
const row = getOtlpSpoolState.get();
|
|
2890
2945
|
const lastDroppedAtMs = row?.last_dropped_at_ms;
|
|
@@ -2898,6 +2953,7 @@ function openObserverDatabase(databaseFile, options = {}) {
|
|
|
2898
2953
|
return {
|
|
2899
2954
|
unit: "logical_bytes",
|
|
2900
2955
|
maxBytes: maxOtlpSpoolBytes,
|
|
2956
|
+
maxAgeMs: maxOtlpSpoolAgeMs,
|
|
2901
2957
|
logicalBytes: requireSpoolInteger(row, "logical_bytes"),
|
|
2902
2958
|
bodyBytes: requireSpoolInteger(row, "body_bytes"),
|
|
2903
2959
|
droppedRecords: requireSpoolInteger(row, "dropped_records"),
|