diff --git a/agrifine-extension/dist/sidebar.js b/agrifine-extension/dist/sidebar.js index 9937731..c17c74d 100644 --- a/agrifine-extension/dist/sidebar.js +++ b/agrifine-extension/dist/sidebar.js @@ -293,6 +293,215 @@ var AgrifineAgent = /*#__PURE__*/function () { /***/ }, +/***/ "./src/ag-refine/committee.js" +/*!************************************!*\ + !*** ./src/ag-refine/committee.js ***! + \************************************/ +(__unused_webpack_module, __webpack_exports__, __webpack_require__) { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ COMMITTEE: () => (/* binding */ COMMITTEE), +/* harmony export */ runCommitteeAgent: () => (/* binding */ runCommitteeAgent) +/* harmony export */ }); +/* harmony import */ var _utils_storage_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils/storage.js */ "./src/utils/storage.js"); +function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); } +function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); } +function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; } +function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } +function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } +function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } +function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } +/** + * The Boardroom β€” Multi-Agent Audit Committee + * + * Four named advisors, each with a domain-specific persona, review the same + * farm data context and report in sequence. Later agents receive a summary of + * what earlier agents said, enabling authentic cross-domain commentary. + */ + +var MODEL = 'claude-sonnet-4-6'; +var ANTHROPIC_URL = 'https://api.anthropic.com/v1/messages'; +var COMMITTEE = [{ + id: 'financials', + name: 'Kount Kuekkens', + role: 'CFO Β· Financials', + emoji: 'πŸ’Ή', + accentColor: '#d97706', + borderStyle: 'border-l-[3px]', + borderColor: '#d97706', + persona: "You are Kount Kuekkens, a retired agricultural economist now serving as CFO for this farm operation. You speak with a spiraling rhetorical style \u2014 you begin broad, drift into economic theory or historical context, but always land on concrete \"Dairy Moneyball\" math that actually matters.\n\nYour domain: Income Over Feed Cost (IOFC), commodity price impacts on margins, feed efficiency ratios, processor quality premium/penalty thresholds, cash flow position, budget variances, and the financial consequences of operational data errors.\n\nWhen you spot data problems, quantify the financial blindspot they create. When you see opportunities, express them in dollar terms. You are candid about when you are \"spiraling\" into uncertainty vs. when you have hard numbers. You occasionally reference obscure economic principles before getting to the point.\n\nReport in 3\u20134 paragraphs. Be specific \u2014 name dollar figures, percentages, and cite the data points you are drawing from." +}, { + id: 'crops', + name: 'Rolf Forage', + role: 'Agronomist Β· Crops', + emoji: '🌾', + accentColor: '#16a34a', + borderColor: '#16a34a', + persona: "You are Rolf Forage (pronounced \"For-ahh-juz\"), a fiercely opinionated agronomist and crops director. You do not care about spreadsheets or financial models \u2014 you care about what is actually in the field and the bunker right now.\n\nYour domain: forage quality (dry matter, NDF, fiber digestibility), silage inventory and fermentation integrity, harvest timing windows, field conditions (soil type, drainage, compaction), cover crop programs, nutrient cycling, and input scheduling.\n\nYou are demanding and direct. If the data shows a crop problem that will compromise feed quality, you say so loudly and insist it be corrected immediately \u2014 you do not sugarcoat risk to protect someone's budget. You will call out the financial team for cutting corners that ultimately cost more in lost production. You speak in practical, field-level language.\n\nReport in 3\u20134 paragraphs. Be opinionated and specific about what needs to happen and when." +}, { + id: 'herd', + name: 'Dr. Vera Hest', + role: 'Chief Veterinarian Β· Herd Health', + emoji: 'πŸ„', + accentColor: '#60a5fa', + borderColor: '#60a5fa', + persona: "You are Dr. Vera Hest, a sharp-witted, data-driven veterinarian and herd health director. You value biological metrics and animal welfare above all else \u2014 and you will challenge any department that proposes to compromise herd health in the name of cost savings or operational convenience.\n\nYour domain: Somatic Cell Count (SCC) trends and penalty risk, Dry Matter Intake (DMI) per cow, Body Condition Score (BCS), transition cow health, Temperature-Humidity Index (THI) and heat stress protocol, milk component trends (fat, protein), reproductive performance, and disease incidence (ketosis, mastitis, lameness, displaced abomasum).\n\nYou connect biological metrics to production outcomes \u2014 a BCS over 3.75 at calving means dystocia and ketosis next month; a THI of 86 means DMI drops 10\u201315% and milk yield follows within 48 hours. You are precise with thresholds, not vague. You speak clinically but translate findings for the group when needed.\n\nReport in 3\u20134 paragraphs. Be incisive. Cite specific thresholds and explain their downstream consequences." +}, { + id: 'personnel', + name: 'Marla Shift', + role: 'Operations Manager Β· Personnel', + emoji: 'πŸ“‹', + accentColor: '#94a3b8', + borderColor: '#94a3b8', + persona: "You are Marla Shift, the operations-hardened manager who oversees labor, personnel scheduling, equipment maintenance, and day-to-day execution. You are the \"reality check\" of the boardroom.\n\nYour domain: labor availability and shift coverage, overtime costs and crew fatigue, equipment uptime and maintenance backlogs, safety compliance, training gaps, and operational root causes of data errors or production misses.\n\nWhen the other advisors make demands \u2014 Rolf needs an early harvest crew, Vera wants manual pen checks every two hours, Kount wants a new validation system built by Friday \u2014 you translate those demands into actual execution requirements: how many people, how many hours, what it costs, and what else will be delayed or skipped to make it happen.\n\nYou provide honest operational explanations (not excuses) for why things went wrong: mechanical failures, staffing gaps during peak periods, training slips under pressure. You are pragmatic, occasionally exasperated, and very good at finding workarounds under real-world constraints.\n\nReport in 3\u20134 paragraphs. Be concrete about labor, time, and resource constraints." +}]; + +/** + * Run a single committee agent and stream their response. + * priorStatements: array of { name, role, text } from agents who already spoke. + * onChunk: called with partial text as it streams in. + */ +function runCommitteeAgent(_x, _x2, _x3, _x4) { + return _runCommitteeAgent.apply(this, arguments); +} +function _runCommitteeAgent() { + _runCommitteeAgent = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(agent, topic, priorStatements, onChunk) { + var apiKey, contextBundle, priorContext, systemPrompt, res, text, reader, decoder, fullText, buffer, _lines$pop, _yield$reader$read, done, value, lines, _iterator, _step, line, payload, _evt$delta, evt, _t; + return _regenerator().w(function (_context) { + while (1) switch (_context.p = _context.n) { + case 0: + _context.n = 1; + return (0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.sessionGet)(_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.KEYS.API_KEY); + case 1: + apiKey = _context.v; + if (apiKey) { + _context.n = 2; + break; + } + throw new Error('No API key set β€” open βš™ Settings to add your Anthropic key.'); + case 2: + _context.n = 3; + return (0,_utils_storage_js__WEBPACK_IMPORTED_MODULE_0__.buildContextBundle)(); + case 3: + contextBundle = _context.v; + priorContext = priorStatements.length > 0 ? "\n\n\u2500\u2500 PRIOR STATEMENTS FROM YOUR COLLEAGUES \u2500\u2500\n".concat(priorStatements.map(function (s) { + return "".concat(s.name, " (").concat(s.role, "):\n").concat(s.text); + }).join('\n\n─────────────────────\n\n')) : ''; + systemPrompt = "".concat(agent.persona, "\n\n\u2500\u2500 FARM DATA CONTEXT \u2500\u2500\n").concat(contextBundle).concat(priorContext, "\n\nYou are presenting your department report at the weekly audit boardroom. Address the meeting topic directly from your domain's perspective. If colleagues have already spoken, you may reference or push back on their points where they intersect with your domain."); + _context.n = 4; + return fetch(ANTHROPIC_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': apiKey, + 'anthropic-version': '2023-06-01', + 'anthropic-dangerous-direct-browser-access': 'true' + }, + body: JSON.stringify({ + model: MODEL, + max_tokens: 1024, + stream: true, + system: systemPrompt, + messages: [{ + role: 'user', + content: topic + }] + }) + }); + case 4: + res = _context.v; + if (res.ok) { + _context.n = 6; + break; + } + _context.n = 5; + return res.text(); + case 5: + text = _context.v; + throw new Error("API ".concat(res.status, ": ").concat(text)); + case 6: + // Stream SSE response + reader = res.body.getReader(); + decoder = new TextDecoder(); + fullText = ''; + buffer = ''; + case 7: + if (false) // removed by dead control flow +{} + _context.n = 8; + return reader.read(); + case 8: + _yield$reader$read = _context.v; + done = _yield$reader$read.done; + value = _yield$reader$read.value; + if (!done) { + _context.n = 9; + break; + } + return _context.a(3, 19); + case 9: + buffer += decoder.decode(value, { + stream: true + }); + lines = buffer.split('\n'); + buffer = (_lines$pop = lines.pop()) !== null && _lines$pop !== void 0 ? _lines$pop : ''; + _iterator = _createForOfIteratorHelper(lines); + _context.p = 10; + _iterator.s(); + case 11: + if ((_step = _iterator.n()).done) { + _context.n = 15; + break; + } + line = _step.value; + if (line.startsWith('data: ')) { + _context.n = 12; + break; + } + return _context.a(3, 14); + case 12: + payload = line.slice(6).trim(); + if (!(payload === '[DONE]')) { + _context.n = 13; + break; + } + return _context.a(3, 14); + case 13: + try { + evt = JSON.parse(payload); + if (evt.type === 'content_block_delta' && ((_evt$delta = evt.delta) === null || _evt$delta === void 0 ? void 0 : _evt$delta.type) === 'text_delta') { + fullText += evt.delta.text; + onChunk(evt.delta.text, fullText); + } + } catch (_) {} + case 14: + _context.n = 11; + break; + case 15: + _context.n = 17; + break; + case 16: + _context.p = 16; + _t = _context.v; + _iterator.e(_t); + case 17: + _context.p = 17; + _iterator.f(); + return _context.f(17); + case 18: + _context.n = 7; + break; + case 19: + return _context.a(2, fullText); + } + }, _callee, null, [[10, 16, 17, 18]]); + })); + return _runCommitteeAgent.apply(this, arguments); +} + +/***/ }, + /***/ "./src/ag-refine/index.js" /*!********************************!*\ !*** ./src/ag-refine/index.js ***! @@ -304,11 +513,18 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ AgRefineModule: () => (/* binding */ AgRefineModule) /* harmony export */ }); /* harmony import */ var _agent_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./agent.js */ "./src/ag-refine/agent.js"); +/* harmony import */ var _committee_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./committee.js */ "./src/ag-refine/committee.js"); +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function _regeneratorValues(e) { if (null != e) { var t = e["function" == typeof Symbol && Symbol.iterator || "@@iterator"], r = 0; if (t) return t.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) return { next: function next() { return e && r >= e.length && (e = void 0), { value: e && e[r++], done: !e }; } }; } throw new TypeError(_typeof(e) + " is not iterable"); } +function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; } +function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } +function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); } function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); } function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } + var TOOL_ICONS = { get_reading_list: 'πŸ“–', get_field_profiles: '🌱', @@ -324,10 +540,20 @@ var TOOL_ICONS = { get_farm_memory: '🧠', update_farm_memory: 'πŸ’Ύ' }; -var SUGGESTED_PROMPTS = ['Review all my farm data and build a farm memory summary', 'What are my current field conditions and harvest windows?', 'What risks or opportunities do you see across my operation?', 'Screenshot this page and tell me what agricultural data you see', 'Export my reading list and field profiles to CSV']; +var AGENT_PROMPTS = ['Review all my farm data and build a farm memory summary', 'What are my current field conditions and harvest windows?', 'What risks or opportunities do you see across my operation?', 'Screenshot this page and tell me what agricultural data you see', 'Export my reading list and field profiles to CSV']; +var BOARDROOM_PROMPTS = ['Weekly operations audit β€” all departments, give me your status', 'We have a data integrity issue β€” assess the impact by department', 'Heat stress event incoming β€” what does each department need?', 'Review all farm data and identify the single biggest risk per department', 'What is the biggest point of contention between departments right now?']; function AgRefineModule() { - var messages = []; - var isRunning = false; + var mode = 'agent'; // 'agent' | 'boardroom' + + // Agent mode state + var agentMessages = []; + var agentRunning = false; + + // Boardroom mode state + var boardMessages = []; // { type: 'topic'|'report'|'chair'|'thinking', ... } + var boardRunning = false; + var boardTargetAgent = null; // null = all, or agent id for targeted response + return { id: 'ag-refine', label: 'AgriAgent', @@ -337,46 +563,116 @@ function AgRefineModule() { return _regenerator().w(function (_context) { while (1) switch (_context.n) { case 0: - container.innerHTML = "\n
\n\n \n
\n
\n \uD83E\uDD16\n

AgriAgent

\n AI Agent\n
\n

Multi-step reasoning over all your farm data

\n
\n\n \n
\n\n \n
\n

Try asking\u2026

\n
\n ".concat(SUGGESTED_PROMPTS.map(function (p) { - return "\n "); - }).join(''), "\n
\n
\n\n \n
\n
\n \n \n
\n \n
\n
\n "); + container.innerHTML = _this._html(); _this._bindEvents(container); - _this._renderMessages(container); + _this._switchMode(mode, container); case 1: return _context.a(2); } }, _callee); }))(); }, + _html: function _html() { + return "\n
\n\n \n
\n \n \n
\n\n \n
\n\n
\n
\n \uD83E\uDD16\n

AgriAgent

\n AI Agent\n
\n

Multi-step reasoning over all your farm data

\n
\n\n
\n\n
\n

Try asking\u2026

\n
\n ".concat(AGENT_PROMPTS.map(function (p) { + return "\n "); + }).join(''), "\n
\n
\n\n
\n
\n \n \n
\n \n
\n
\n\n \n
\n\n \n
\n
\n \uD83C\uDFDB\uFE0F\n

The Boardroom

\n
\n
\n ").concat(_committee_js__WEBPACK_IMPORTED_MODULE_1__.COMMITTEE.map(function (a) { + return "\n
\n ").concat(a.emoji, "\n ").concat(a.name, "\n
\n "); + }).join(''), "\n
\n
\n\n \n
\n\n \n
\n

Call the meeting to order\u2026

\n
\n ").concat(BOARDROOM_PROMPTS.map(function (p) { + return "\n "); + }).join(''), "\n
\n
\n\n \n
\n \n
\n Address:\n \n ").concat(_committee_js__WEBPACK_IMPORTED_MODULE_1__.COMMITTEE.map(function (a) { + return "\n \n "); + }).join(''), "\n
\n\n
\n \n \n
\n \n
\n
\n\n
\n "); + }, + _switchMode: function _switchMode(newMode, container) { + mode = newMode; + container.querySelector('#panel-agent').classList.toggle('hidden', mode !== 'agent'); + container.querySelector('#panel-boardroom').classList.toggle('hidden', mode !== 'boardroom'); + container.querySelectorAll('.mode-btn').forEach(function (btn) { + var active = btn.dataset.mode === mode; + btn.style.color = active ? '#22c55e' : '#3d4f66'; + btn.style.borderBottom = active ? '2px solid #22c55e' : '2px solid transparent'; + }); + }, _bindEvents: function _bindEvents(container) { var _this2 = this; - var input = container.querySelector('#agent-input'); - var sendBtn = container.querySelector('#agent-send'); - var send = function send() { - var text = input.value.trim(); - if (!text || isRunning) return; - input.value = ''; + // Mode toggle + container.querySelectorAll('.mode-btn').forEach(function (btn) { + btn.addEventListener('click', function () { + return _this2._switchMode(btn.dataset.mode, container); + }); + }); + + // ── Agent mode ── + var agentInput = container.querySelector('#agent-input'); + var agentSend = container.querySelector('#agent-send'); + var sendAgent = function sendAgent() { + var text = agentInput.value.trim(); + if (!text || agentRunning) return; + agentInput.value = ''; _this2._runAgent(text, container); }; - sendBtn.addEventListener('click', send); - input.addEventListener('keydown', function (e) { + agentSend.addEventListener('click', sendAgent); + agentInput.addEventListener('keydown', function (e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); - send(); + sendAgent(); } }); - container.querySelectorAll('.suggest-btn').forEach(function (btn) { + container.querySelectorAll('.agent-suggest-btn').forEach(function (btn) { btn.addEventListener('click', function () { - input.value = btn.textContent.trim(); - send(); + agentInput.value = btn.textContent.trim(); + sendAgent(); }); }); container.querySelector('#agent-clear').addEventListener('click', function () { - messages = []; - isRunning = false; - _this2._renderMessages(container); + agentMessages = []; + agentRunning = false; + _this2._renderAgentMessages(container); + }); + + // ── Boardroom mode ── + var boardInput = container.querySelector('#board-input'); + var boardSend = container.querySelector('#board-send'); + var sendBoard = function sendBoard() { + var text = boardInput.value.trim(); + if (!text || boardRunning) return; + boardInput.value = ''; + _this2._runBoardroom(text, container); + }; + boardSend.addEventListener('click', sendBoard); + boardInput.addEventListener('keydown', function (e) { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + sendBoard(); + } + }); + container.querySelectorAll('.board-suggest-btn').forEach(function (btn) { + btn.addEventListener('click', function () { + boardInput.value = btn.textContent.trim(); + sendBoard(); + }); + }); + container.querySelector('#board-clear').addEventListener('click', function () { + boardMessages = []; + boardRunning = false; + boardTargetAgent = null; + container.querySelector('#board-target-bar').classList.add('hidden'); + _this2._renderBoardMessages(container); + }); + + // Target agent selector + container.querySelectorAll('.target-btn').forEach(function (btn) { + btn.addEventListener('click', function () { + boardTargetAgent = btn.dataset.target === 'all' ? null : btn.dataset.target; + container.querySelectorAll('.target-btn').forEach(function (b) { + b.classList.toggle('active-target', b.dataset.target === (boardTargetAgent !== null && boardTargetAgent !== void 0 ? boardTargetAgent : 'all')); + b.style.borderColor = b.classList.contains('active-target') ? '#22c55e' : ''; + if (!b.classList.contains('active-target')) b.style.borderColor = '#253047'; + }); + }); }); }, + // ── Agent mode logic ────────────────────────────────────────────────────── _runAgent: function _runAgent(userText, container) { var _this3 = this; return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() { @@ -385,33 +681,27 @@ function AgRefineModule() { return _regenerator().w(function (_context2) { while (1) switch (_context2.p = _context2.n) { case 0: - if (!isRunning) { + if (!agentRunning) { _context2.n = 1; break; } return _context2.a(2); case 1: - isRunning = true; - - // Hide suggestions + agentRunning = true; (_container$querySelec = container.querySelector('#agent-suggestions')) === null || _container$querySelec === void 0 || _container$querySelec.classList.add('hidden'); - - // Add user message - messages.push({ + agentMessages.push({ role: 'user', text: userText }); - _this3._renderMessages(container); - - // Thinking placeholder + _this3._renderAgentMessages(container); thinkingId = "thinking_".concat(Date.now()); - messages.push({ + agentMessages.push({ role: 'thinking', id: thinkingId, steps: [] }); - _this3._renderMessages(container); - thinkingMsg = messages[messages.length - 1]; + _this3._renderAgentMessages(container); + thinkingMsg = agentMessages[agentMessages.length - 1]; agent = new _agent_js__WEBPACK_IMPORTED_MODULE_0__.AgrifineAgent({ onEvent: function onEvent(_ref) { var type = _ref.type, @@ -433,28 +723,27 @@ function AgRefineModule() { var last = thinkingMsg.steps[thinkingMsg.steps.length - 1]; if ((last === null || last === void 0 ? void 0 : last.type) === 'tool') last.done = true; } else if (type === 'answer') { - // Replace thinking bubble with final answer - var idx = messages.findIndex(function (m) { + var idx = agentMessages.findIndex(function (m) { return m.id === thinkingId; }); - if (idx >= 0) messages.splice(idx, 1); - messages.push({ + if (idx >= 0) agentMessages.splice(idx, 1); + agentMessages.push({ role: 'assistant', text: data }); - isRunning = false; + agentRunning = false; } else if (type === 'error') { - var _idx = messages.findIndex(function (m) { + var _idx = agentMessages.findIndex(function (m) { return m.id === thinkingId; }); - if (_idx >= 0) messages.splice(_idx, 1); - messages.push({ + if (_idx >= 0) agentMessages.splice(_idx, 1); + agentMessages.push({ role: 'error', text: data }); - isRunning = false; + agentRunning = false; } - _this3._renderMessages(container); + _this3._renderAgentMessages(container); } }); _context2.p = 2; @@ -466,58 +755,225 @@ function AgRefineModule() { case 4: _context2.p = 4; _t = _context2.v; - idx = messages.findIndex(function (m) { + idx = agentMessages.findIndex(function (m) { return m.id === thinkingId; }); - if (idx >= 0) messages.splice(idx, 1); - messages.push({ + if (idx >= 0) agentMessages.splice(idx, 1); + agentMessages.push({ role: 'error', text: _t.message }); - isRunning = false; - _this3._renderMessages(container); + agentRunning = false; + _this3._renderAgentMessages(container); case 5: return _context2.a(2); } }, _callee2, null, [[2, 4]]); }))(); }, - _renderMessages: function _renderMessages(container) { + _renderAgentMessages: function _renderAgentMessages(container) { var chat = container.querySelector('#agent-chat'); if (!chat) return; - if (messages.length === 0) { + if (agentMessages.length === 0) { var _container$querySelec2; chat.innerHTML = ''; (_container$querySelec2 = container.querySelector('#agent-suggestions')) === null || _container$querySelec2 === void 0 || _container$querySelec2.classList.remove('hidden'); return; } - chat.innerHTML = messages.map(function (msg) { + chat.innerHTML = agentMessages.map(function (msg) { if (msg.role === 'user') { - return "\n
\n
\n ".concat(escapeHtml(msg.text), "\n
\n
"); + return "
\n
\n ".concat(escapeHtml(msg.text), "\n
\n
"); } if (msg.role === 'thinking') { var _msg$steps; var steps = (_msg$steps = msg.steps) !== null && _msg$steps !== void 0 ? _msg$steps : []; - return "\n
\n ".concat(steps.map(function (step) { + return "
\n ".concat(steps.map(function (step) { if (step.type === 'status') { - return "
\n ".concat(escapeHtml(step.text), "\n
"); + return "
\n ".concat(escapeHtml(step.text), "\n
"); } if (step.type === 'tool') { - return "
\n ".concat(step.icon, "\n ").concat(step.name, "\n ").concat(step.done ? 'βœ“' : '', "\n
"); + return "
\n ".concat(step.icon, "\n ").concat(step.name, "\n ").concat(step.done ? 'βœ“' : '', "\n
"); } return ''; - }).join(''), "\n ").concat(steps.length === 0 ? '
Starting…
' : '', "\n
"); + }).join(''), "\n ").concat(steps.length === 0 ? '
Starting…
' : '', "\n
"); } if (msg.role === 'assistant') { - return "\n
\n
\uD83E\uDD16
\n
\n ".concat(escapeHtml(msg.text), "\n
\n
"); + return "
\n
\uD83E\uDD16
\n
".concat(escapeHtml(msg.text), "
\n
"); } if (msg.role === 'error') { - return "\n
\n \u26A0\uFE0F ".concat(escapeHtml(msg.text), "\n
"); + return "
\u26A0\uFE0F ".concat(escapeHtml(msg.text), "
"); } return ''; }).join(''); + chat.scrollTop = chat.scrollHeight; + }, + // ── Boardroom mode logic ────────────────────────────────────────────────── + _runBoardroom: function _runBoardroom(topic, container) { + var _this4 = this; + return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() { + var _container$querySelec3; + var agentsToRun, priorStatements, _iterator, _step, agent, _iterator2, _step2, _loop, _t3; + return _regenerator().w(function (_context4) { + while (1) switch (_context4.p = _context4.n) { + case 0: + if (!boardRunning) { + _context4.n = 1; + break; + } + return _context4.a(2); + case 1: + boardRunning = true; + (_container$querySelec3 = container.querySelector('#board-suggestions')) === null || _container$querySelec3 === void 0 || _container$querySelec3.classList.add('hidden'); - // Scroll to bottom + // Add chair statement + boardMessages.push({ + type: 'chair', + text: topic + }); + _this4._renderBoardMessages(container); + + // Determine which agents respond + agentsToRun = boardTargetAgent ? _committee_js__WEBPACK_IMPORTED_MODULE_1__.COMMITTEE.filter(function (a) { + return a.id === boardTargetAgent; + }) : _committee_js__WEBPACK_IMPORTED_MODULE_1__.COMMITTEE; // Build context: full prior statements from this meeting for sequential passing + priorStatements = boardMessages.filter(function (m) { + return m.type === 'report' && m.text; + }).map(function (m) { + return { + name: m.agent.name, + role: m.agent.role, + text: m.text + }; + }); // Add thinking placeholders for each agent that will speak + _iterator = _createForOfIteratorHelper(agentsToRun); + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + agent = _step.value; + boardMessages.push({ + type: 'thinking', + agentId: agent.id, + agent: agent + }); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + _this4._renderBoardMessages(container); + + // Run agents sequentially so each gets the prior context + _iterator2 = _createForOfIteratorHelper(agentsToRun); + _context4.p = 2; + _loop = /*#__PURE__*/_regenerator().m(function _loop() { + var agent, msgIdx, reportMsg, _t2; + return _regenerator().w(function (_context3) { + while (1) switch (_context3.p = _context3.n) { + case 0: + agent = _step2.value; + msgIdx = boardMessages.findIndex(function (m) { + return m.type === 'thinking' && m.agentId === agent.id; + }); // Convert thinking β†’ streaming report + reportMsg = { + type: 'report', + agent: agent, + text: '', + streaming: true + }; + if (msgIdx >= 0) boardMessages.splice(msgIdx, 1, reportMsg); + _this4._renderBoardMessages(container); + _context3.p = 1; + _context3.n = 2; + return (0,_committee_js__WEBPACK_IMPORTED_MODULE_1__.runCommitteeAgent)(agent, topic, priorStatements, function (chunk, fullText) { + reportMsg.text = fullText; + _this4._renderBoardMessages(container); + }); + case 2: + reportMsg.streaming = false; + priorStatements.push({ + name: agent.name, + role: agent.role, + text: reportMsg.text + }); + _context3.n = 4; + break; + case 3: + _context3.p = 3; + _t2 = _context3.v; + boardMessages.splice(boardMessages.indexOf(reportMsg), 1, { + type: 'error', + text: "".concat(agent.name, ": ").concat(_t2.message) + }); + case 4: + _this4._renderBoardMessages(container); + case 5: + return _context3.a(2); + } + }, _loop, null, [[1, 3]]); + }); + _iterator2.s(); + case 3: + if ((_step2 = _iterator2.n()).done) { + _context4.n = 5; + break; + } + return _context4.d(_regeneratorValues(_loop()), 4); + case 4: + _context4.n = 3; + break; + case 5: + _context4.n = 7; + break; + case 6: + _context4.p = 6; + _t3 = _context4.v; + _iterator2.e(_t3); + case 7: + _context4.p = 7; + _iterator2.f(); + return _context4.f(7); + case 8: + boardRunning = false; + + // Show target bar after the first meeting round + if (boardMessages.some(function (m) { + return m.type === 'report'; + })) { + container.querySelector('#board-target-bar').classList.remove('hidden'); + } + _this4._renderBoardMessages(container); + case 9: + return _context4.a(2); + } + }, _callee3, null, [[2, 6, 7, 8]]); + }))(); + }, + _renderBoardMessages: function _renderBoardMessages(container) { + var chat = container.querySelector('#board-chat'); + if (!chat) return; + if (boardMessages.length === 0) { + var _container$querySelec4; + chat.innerHTML = ''; + (_container$querySelec4 = container.querySelector('#board-suggestions')) === null || _container$querySelec4 === void 0 || _container$querySelec4.classList.remove('hidden'); + return; + } + chat.innerHTML = boardMessages.map(function (msg) { + if (msg.type === 'chair') { + return "\n
\n
\n
Chair \xB7 David
\n ".concat(escapeHtml(msg.text), "\n
\n
"); + } + if (msg.type === 'thinking') { + var a = msg.agent; + return "\n
\n
\n ").concat(a.emoji, "\n ").concat(a.name, "\n ").concat(a.role, "\n \n
\n
\n Reviewing data\u2026\n
\n
"); + } + if (msg.type === 'report') { + var _a = msg.agent; + return "\n
\n
\n ").concat(_a.emoji, "\n ").concat(_a.name, "\n ").concat(_a.role, "\n ").concat(msg.streaming ? '' : '', "\n
\n
\n ").concat(escapeHtml(msg.text || '…'), "\n
\n
"); + } + if (msg.type === 'error') { + return "
\u26A0\uFE0F ".concat(escapeHtml(msg.text), "
"); + } + return ''; + }).join(''); chat.scrollTop = chat.scrollHeight; } }; diff --git a/agrifine-extension/screenshots/agent_mode.png b/agrifine-extension/screenshots/agent_mode.png new file mode 100644 index 0000000..1235abd Binary files /dev/null and b/agrifine-extension/screenshots/agent_mode.png differ diff --git a/agrifine-extension/screenshots/boardroom_mode.png b/agrifine-extension/screenshots/boardroom_mode.png new file mode 100644 index 0000000..9c0d528 Binary files /dev/null and b/agrifine-extension/screenshots/boardroom_mode.png differ diff --git a/agrifine-extension/src/ag-refine/committee.js b/agrifine-extension/src/ag-refine/committee.js new file mode 100644 index 0000000..09ecdfc --- /dev/null +++ b/agrifine-extension/src/ag-refine/committee.js @@ -0,0 +1,153 @@ +/** + * The Boardroom β€” Multi-Agent Audit Committee + * + * Four named advisors, each with a domain-specific persona, review the same + * farm data context and report in sequence. Later agents receive a summary of + * what earlier agents said, enabling authentic cross-domain commentary. + */ +import { sessionGet, KEYS, buildContextBundle } from '../utils/storage.js'; + +const MODEL = 'claude-sonnet-4-6'; +const ANTHROPIC_URL = 'https://api.anthropic.com/v1/messages'; + +export const COMMITTEE = [ + { + id: 'financials', + name: 'Kount Kuekkens', + role: 'CFO Β· Financials', + emoji: 'πŸ’Ή', + accentColor: '#d97706', + borderStyle: 'border-l-[3px]', + borderColor: '#d97706', + persona: `You are Kount Kuekkens, a retired agricultural economist now serving as CFO for this farm operation. You speak with a spiraling rhetorical style β€” you begin broad, drift into economic theory or historical context, but always land on concrete "Dairy Moneyball" math that actually matters. + +Your domain: Income Over Feed Cost (IOFC), commodity price impacts on margins, feed efficiency ratios, processor quality premium/penalty thresholds, cash flow position, budget variances, and the financial consequences of operational data errors. + +When you spot data problems, quantify the financial blindspot they create. When you see opportunities, express them in dollar terms. You are candid about when you are "spiraling" into uncertainty vs. when you have hard numbers. You occasionally reference obscure economic principles before getting to the point. + +Report in 3–4 paragraphs. Be specific β€” name dollar figures, percentages, and cite the data points you are drawing from.`, + }, + { + id: 'crops', + name: 'Rolf Forage', + role: 'Agronomist Β· Crops', + emoji: '🌾', + accentColor: '#16a34a', + borderColor: '#16a34a', + persona: `You are Rolf Forage (pronounced "For-ahh-juz"), a fiercely opinionated agronomist and crops director. You do not care about spreadsheets or financial models β€” you care about what is actually in the field and the bunker right now. + +Your domain: forage quality (dry matter, NDF, fiber digestibility), silage inventory and fermentation integrity, harvest timing windows, field conditions (soil type, drainage, compaction), cover crop programs, nutrient cycling, and input scheduling. + +You are demanding and direct. If the data shows a crop problem that will compromise feed quality, you say so loudly and insist it be corrected immediately β€” you do not sugarcoat risk to protect someone's budget. You will call out the financial team for cutting corners that ultimately cost more in lost production. You speak in practical, field-level language. + +Report in 3–4 paragraphs. Be opinionated and specific about what needs to happen and when.`, + }, + { + id: 'herd', + name: 'Dr. Vera Hest', + role: 'Chief Veterinarian Β· Herd Health', + emoji: 'πŸ„', + accentColor: '#60a5fa', + borderColor: '#60a5fa', + persona: `You are Dr. Vera Hest, a sharp-witted, data-driven veterinarian and herd health director. You value biological metrics and animal welfare above all else β€” and you will challenge any department that proposes to compromise herd health in the name of cost savings or operational convenience. + +Your domain: Somatic Cell Count (SCC) trends and penalty risk, Dry Matter Intake (DMI) per cow, Body Condition Score (BCS), transition cow health, Temperature-Humidity Index (THI) and heat stress protocol, milk component trends (fat, protein), reproductive performance, and disease incidence (ketosis, mastitis, lameness, displaced abomasum). + +You connect biological metrics to production outcomes β€” a BCS over 3.75 at calving means dystocia and ketosis next month; a THI of 86 means DMI drops 10–15% and milk yield follows within 48 hours. You are precise with thresholds, not vague. You speak clinically but translate findings for the group when needed. + +Report in 3–4 paragraphs. Be incisive. Cite specific thresholds and explain their downstream consequences.`, + }, + { + id: 'personnel', + name: 'Marla Shift', + role: 'Operations Manager Β· Personnel', + emoji: 'πŸ“‹', + accentColor: '#94a3b8', + borderColor: '#94a3b8', + persona: `You are Marla Shift, the operations-hardened manager who oversees labor, personnel scheduling, equipment maintenance, and day-to-day execution. You are the "reality check" of the boardroom. + +Your domain: labor availability and shift coverage, overtime costs and crew fatigue, equipment uptime and maintenance backlogs, safety compliance, training gaps, and operational root causes of data errors or production misses. + +When the other advisors make demands β€” Rolf needs an early harvest crew, Vera wants manual pen checks every two hours, Kount wants a new validation system built by Friday β€” you translate those demands into actual execution requirements: how many people, how many hours, what it costs, and what else will be delayed or skipped to make it happen. + +You provide honest operational explanations (not excuses) for why things went wrong: mechanical failures, staffing gaps during peak periods, training slips under pressure. You are pragmatic, occasionally exasperated, and very good at finding workarounds under real-world constraints. + +Report in 3–4 paragraphs. Be concrete about labor, time, and resource constraints.`, + }, +]; + +/** + * Run a single committee agent and stream their response. + * priorStatements: array of { name, role, text } from agents who already spoke. + * onChunk: called with partial text as it streams in. + */ +export async function runCommitteeAgent(agent, topic, priorStatements, onChunk) { + const apiKey = await sessionGet(KEYS.API_KEY); + if (!apiKey) throw new Error('No API key set β€” open βš™ Settings to add your Anthropic key.'); + + const contextBundle = await buildContextBundle(); + + const priorContext = priorStatements.length > 0 + ? `\n\n── PRIOR STATEMENTS FROM YOUR COLLEAGUES ──\n${priorStatements.map( + (s) => `${s.name} (${s.role}):\n${s.text}` + ).join('\n\n─────────────────────\n\n')}` + : ''; + + const systemPrompt = `${agent.persona} + +── FARM DATA CONTEXT ── +${contextBundle}${priorContext} + +You are presenting your department report at the weekly audit boardroom. Address the meeting topic directly from your domain's perspective. If colleagues have already spoken, you may reference or push back on their points where they intersect with your domain.`; + + const res = await fetch(ANTHROPIC_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': apiKey, + 'anthropic-version': '2023-06-01', + 'anthropic-dangerous-direct-browser-access': 'true', + }, + body: JSON.stringify({ + model: MODEL, + max_tokens: 1024, + stream: true, + system: systemPrompt, + messages: [{ role: 'user', content: topic }], + }), + }); + + if (!res.ok) { + const text = await res.text(); + throw new Error(`API ${res.status}: ${text}`); + } + + // Stream SSE response + const reader = res.body.getReader(); + const decoder = new TextDecoder(); + let fullText = ''; + let buffer = ''; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + buffer = lines.pop() ?? ''; + + for (const line of lines) { + if (!line.startsWith('data: ')) continue; + const payload = line.slice(6).trim(); + if (payload === '[DONE]') continue; + try { + const evt = JSON.parse(payload); + if (evt.type === 'content_block_delta' && evt.delta?.type === 'text_delta') { + fullText += evt.delta.text; + onChunk(evt.delta.text, fullText); + } + } catch (_) {} + } + } + + return fullText; +} diff --git a/agrifine-extension/src/ag-refine/index.js b/agrifine-extension/src/ag-refine/index.js index 718eb07..fed6ae1 100644 --- a/agrifine-extension/src/ag-refine/index.js +++ b/agrifine-extension/src/ag-refine/index.js @@ -1,4 +1,5 @@ import { AgrifineAgent } from './agent.js'; +import { COMMITTEE, runCommitteeAgent } from './committee.js'; const TOOL_ICONS = { get_reading_list: 'πŸ“–', @@ -16,7 +17,7 @@ const TOOL_ICONS = { update_farm_memory: 'πŸ’Ύ', }; -const SUGGESTED_PROMPTS = [ +const AGENT_PROMPTS = [ 'Review all my farm data and build a farm memory summary', 'What are my current field conditions and harvest windows?', 'What risks or opportunities do you see across my operation?', @@ -24,111 +25,264 @@ const SUGGESTED_PROMPTS = [ 'Export my reading list and field profiles to CSV', ]; +const BOARDROOM_PROMPTS = [ + 'Weekly operations audit β€” all departments, give me your status', + 'We have a data integrity issue β€” assess the impact by department', + 'Heat stress event incoming β€” what does each department need?', + 'Review all farm data and identify the single biggest risk per department', + 'What is the biggest point of contention between departments right now?', +]; + export function AgRefineModule() { - let messages = []; - let isRunning = false; + let mode = 'agent'; // 'agent' | 'boardroom' + + // Agent mode state + let agentMessages = []; + let agentRunning = false; + + // Boardroom mode state + let boardMessages = []; // { type: 'topic'|'report'|'chair'|'thinking', ... } + let boardRunning = false; + let boardTargetAgent = null; // null = all, or agent id for targeted response return { id: 'ag-refine', label: 'AgriAgent', async render(container) { - container.innerHTML = ` + container.innerHTML = this._html(); + this._bindEvents(container); + this._switchMode(mode, container); + }, + + _html() { + return `
- -
-
- πŸ€– -

AgriAgent

- AI Agent -
-

Multi-step reasoning over all your farm data

+ +
+ +
- -
+ +