@bolloon/bolloon-agent 0.6.6 → 0.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/ha-daemon.js +85 -0
- package/dist/agents/ha-supervisor.js +146 -0
- package/dist/agents/memory-store.js +25 -0
- package/dist/agents/opportunity-match.js +15 -3
- package/dist/agents/pi-sdk-session-manager.js +8 -0
- package/dist/agents/pi-sdk.js +67 -494
- package/dist/bootstrap/context-collector.js +65 -3
- package/dist/bootstrap/persona-loader.js +26 -0
- package/dist/electron-build/agents/memory-store.js +26 -0
- package/dist/electron-build/agents/memory-store.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-session-manager.js +8 -0
- package/dist/electron-build/agents/pi-sdk-session-manager.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk.js +65 -492
- package/dist/electron-build/agents/pi-sdk.js.map +1 -1
- package/dist/electron-build/bootstrap/context-collector.js +66 -3
- package/dist/electron-build/bootstrap/context-collector.js.map +1 -1
- package/dist/electron-build/bootstrap/persona-loader.js +27 -0
- package/dist/electron-build/bootstrap/persona-loader.js.map +1 -1
- package/dist/electron-build/llm/pi-ai.js +16 -3
- package/dist/electron-build/llm/pi-ai.js.map +1 -1
- package/dist/electron-build/llm/system-prompt/registry.js +8 -6
- package/dist/electron-build/llm/system-prompt/registry.js.map +1 -1
- package/dist/ios/bolloon-web.json +3 -3
- package/dist/ios/index.html +19 -4
- package/dist/ios/mobile-agent.js +267 -41
- package/dist/ios/mobile-contacts.js +0 -4
- package/dist/ios/mobile-core.js +806 -30
- package/dist/ios/mobile-memory.js +317 -0
- package/dist/ios/mobile.html +19 -4
- package/dist/ios/mobile.js +144 -30
- package/dist/ios/routes-contacts.js +94 -1
- package/dist/ios/routes-world.js +9 -1
- package/dist/ios/server.js +51 -40
- package/dist/ios/sw.js +1 -1
- package/dist/kernel/gate-scan.js +1 -1
- package/dist/kernel/ha/ha-arbiter.js +177 -0
- package/dist/kernel/ha/ha-guard.js +151 -0
- package/dist/kernel/index.js +3 -0
- package/dist/kernel/plan.js +5 -5
- package/dist/kernel/roster.js +3 -3
- package/dist/llm/pi-ai.js +16 -3
- package/dist/llm/system-prompt/registry.js +8 -6
- package/dist/web/bolloon-web.json +3 -3
- package/dist/web/mobile-agent.js +265 -44
- package/dist/web/mobile-core.js +681 -32
- package/dist/web/mobile-memory.js +317 -0
- package/dist/web/mobile.html +4 -4
- package/dist/web/mobile.js +36 -27
- package/dist/web/routes-world.js +9 -1
- package/dist/web/server.js +50 -39
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
package/dist/ios/mobile-core.js
CHANGED
|
@@ -623,6 +623,307 @@
|
|
|
623
623
|
}
|
|
624
624
|
});
|
|
625
625
|
|
|
626
|
+
// src/web/mobile-memory.ts
|
|
627
|
+
var mobile_memory_exports = {};
|
|
628
|
+
__export(mobile_memory_exports, {
|
|
629
|
+
MobileCache: () => MobileCache,
|
|
630
|
+
MobileMemory: () => MobileMemory,
|
|
631
|
+
classifyRelation: () => classifyRelation,
|
|
632
|
+
heuristicImportance: () => heuristicImportance,
|
|
633
|
+
quickHash: () => quickHash,
|
|
634
|
+
validMemoryContent: () => validMemoryContent
|
|
635
|
+
});
|
|
636
|
+
function quickHash(text) {
|
|
637
|
+
let h3 = 2166136261;
|
|
638
|
+
for (let i = 0; i < String(text || "").length; i++) {
|
|
639
|
+
h3 ^= String(text || "").charCodeAt(i);
|
|
640
|
+
h3 = Math.imul(h3, 16777619);
|
|
641
|
+
}
|
|
642
|
+
return (h3 >>> 0).toString(36) + "_" + String(text || "").length.toString(36);
|
|
643
|
+
}
|
|
644
|
+
function validMemoryContent(content) {
|
|
645
|
+
return String(content || "").trim().length >= 2;
|
|
646
|
+
}
|
|
647
|
+
function heuristicImportance(content, kind = "fact") {
|
|
648
|
+
const t = String(content || "");
|
|
649
|
+
let score = 0.3;
|
|
650
|
+
if (t.length > 100) score += 0.2;
|
|
651
|
+
if (/喜欢|偏好|讨厌|禁止|必须|永远|从不|重要|注意|记住|约定|家人|朋友|同事/.test(t)) score += 0.25;
|
|
652
|
+
if (kind === "preference" || kind === "relationship") score += 0.2;
|
|
653
|
+
if (kind === "skill-feedback") score += 0.15;
|
|
654
|
+
return Math.min(1, score);
|
|
655
|
+
}
|
|
656
|
+
function classifyRelation(text, source = "") {
|
|
657
|
+
const t = String(text || "").toLowerCase();
|
|
658
|
+
const src4 = String(source || "").toLowerCase();
|
|
659
|
+
if (/家人|妈妈|爸爸|母亲|父亲|儿子|女儿|老婆|老公|妻子|丈夫|兄弟|姐妹/.test(t)) return "family";
|
|
660
|
+
if (/同事|客户|供应商|老板|领导|下属|同事|合作/.test(t)) return "work";
|
|
661
|
+
if (/朋友|好友|哥们|闺蜜|兄弟|认识的人/.test(t)) return "friend";
|
|
662
|
+
if (/directory|phone|手机号|通讯录/.test(src4)) return "acquaintance";
|
|
663
|
+
return "stranger";
|
|
664
|
+
}
|
|
665
|
+
var memKey, MobileMemory, MobileCache;
|
|
666
|
+
var init_mobile_memory = __esm({
|
|
667
|
+
"src/web/mobile-memory.ts"() {
|
|
668
|
+
"use strict";
|
|
669
|
+
memKey = (agentId) => `bolloon_mobile_memory_${agentId}`;
|
|
670
|
+
MobileMemory = class {
|
|
671
|
+
agentId;
|
|
672
|
+
entries = [];
|
|
673
|
+
loaded = false;
|
|
674
|
+
constructor(agentId) {
|
|
675
|
+
this.agentId = String(agentId || "");
|
|
676
|
+
}
|
|
677
|
+
key() {
|
|
678
|
+
return memKey(this.agentId);
|
|
679
|
+
}
|
|
680
|
+
readAll() {
|
|
681
|
+
try {
|
|
682
|
+
const raw = typeof localStorage !== "undefined" ? localStorage.getItem(this.key()) : null;
|
|
683
|
+
if (!raw) return [];
|
|
684
|
+
const parsed = JSON.parse(raw);
|
|
685
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
686
|
+
} catch {
|
|
687
|
+
return [];
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
writeAll(list) {
|
|
691
|
+
try {
|
|
692
|
+
if (typeof localStorage !== "undefined") localStorage.setItem(this.key(), JSON.stringify(list.slice(-200)));
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
ensureLoaded() {
|
|
697
|
+
if (!this.loaded) {
|
|
698
|
+
this.entries = this.readAll();
|
|
699
|
+
this.loaded = true;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
/** 全部条目 (内部用) */
|
|
703
|
+
all() {
|
|
704
|
+
this.ensureLoaded();
|
|
705
|
+
return [...this.entries];
|
|
706
|
+
}
|
|
707
|
+
/** 落一条记忆 (幂等: 同 id 覆盖); 返回条目 */
|
|
708
|
+
remember(input) {
|
|
709
|
+
this.ensureLoaded();
|
|
710
|
+
const content = String(input.content || "").trim();
|
|
711
|
+
const source = input.source || "session";
|
|
712
|
+
if (!validMemoryContent(content)) {
|
|
713
|
+
const id2 = quickHash(this.agentId + "|" + content || "empty");
|
|
714
|
+
const stub = { id: id2, agentId: this.agentId, content: content || "\uFF08\u7A7A\uFF09", kind: input.kind || "fact", importance: 0, createdAt: (/* @__PURE__ */ new Date()).toISOString(), updatedAt: (/* @__PURE__ */ new Date()).toISOString(), source, recallCount: 0 };
|
|
715
|
+
return stub;
|
|
716
|
+
}
|
|
717
|
+
const kind = input.kind || "fact";
|
|
718
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
719
|
+
const id = quickHash(this.agentId + "|" + content);
|
|
720
|
+
const importance = typeof input.importance === "number" ? Math.min(1, Math.max(0, input.importance)) : heuristicImportance(content, kind);
|
|
721
|
+
const entry = {
|
|
722
|
+
id,
|
|
723
|
+
agentId: this.agentId,
|
|
724
|
+
content,
|
|
725
|
+
kind,
|
|
726
|
+
importance,
|
|
727
|
+
createdAt: now,
|
|
728
|
+
updatedAt: now,
|
|
729
|
+
source,
|
|
730
|
+
ttlMs: input.ttlMs,
|
|
731
|
+
decayed: false,
|
|
732
|
+
crystallized: false,
|
|
733
|
+
recallCount: 0,
|
|
734
|
+
...kind === "relationship" && input.relation ? { relation: input.relation } : {}
|
|
735
|
+
};
|
|
736
|
+
const idx = this.entries.findIndex((e) => e.id === id);
|
|
737
|
+
if (idx >= 0) {
|
|
738
|
+
const prev = this.entries[idx];
|
|
739
|
+
this.entries[idx] = { ...prev, ...entry, createdAt: prev.createdAt, recallCount: prev.recallCount, crystallized: prev.crystallized || entry.importance >= 0.8 };
|
|
740
|
+
if (input.relation && prev.relation) {
|
|
741
|
+
this.entries[idx].relation = {
|
|
742
|
+
did: input.relation.did || prev.relation.did,
|
|
743
|
+
kind: input.relation.kind || prev.relation.kind,
|
|
744
|
+
trust: Math.min(1, Math.max(0, (prev.relation.trust + input.relation.trust) / 2))
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
} else {
|
|
748
|
+
this.entries.push(entry);
|
|
749
|
+
}
|
|
750
|
+
this.writeAll(this.entries);
|
|
751
|
+
return this.entries.find((e) => e.id === id) || entry;
|
|
752
|
+
}
|
|
753
|
+
/** 召回: 关键词 + importance 加权; 命中即 +1 recallCount + 刷新 lastRecalledAt (迭代) */
|
|
754
|
+
recall(query, opts = {}) {
|
|
755
|
+
this.ensureLoaded();
|
|
756
|
+
this.applyTtl();
|
|
757
|
+
const q = String(query || "").toLowerCase();
|
|
758
|
+
const limit = opts.limit ?? 3;
|
|
759
|
+
const minScore = opts.minScore ?? 0;
|
|
760
|
+
const scored = [];
|
|
761
|
+
for (const e of this.entries) {
|
|
762
|
+
if (e.decayed) continue;
|
|
763
|
+
const c = String(e.content || "").toLowerCase();
|
|
764
|
+
let s2 = e.importance * 0.5;
|
|
765
|
+
const qTokens = q.split(/\s+/).filter((t) => t.length >= 2);
|
|
766
|
+
const hit = qTokens.filter((t) => c.includes(t)).length;
|
|
767
|
+
if (hit > 0) s2 += 0.3 * (hit / Math.max(qTokens.length, 1));
|
|
768
|
+
if (qTokens.length && qTokens.some((t) => c.includes(t))) s2 += 0.2;
|
|
769
|
+
if (e.relation && e.relation.trust >= 0.7) s2 += 0.1;
|
|
770
|
+
if (s2 >= minScore) scored.push({ e, s: s2 });
|
|
771
|
+
}
|
|
772
|
+
scored.sort((a, b) => b.s - a.s);
|
|
773
|
+
const hits = scored.slice(0, limit).map((x) => x.e);
|
|
774
|
+
for (const h3 of hits) {
|
|
775
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
776
|
+
h3.recallCount += 1;
|
|
777
|
+
h3.lastRecalledAt = now;
|
|
778
|
+
if (h3.recallCount >= 3) h3.crystallized = true;
|
|
779
|
+
}
|
|
780
|
+
if (hits.length) this.writeAll(this.entries);
|
|
781
|
+
return hits;
|
|
782
|
+
}
|
|
783
|
+
/** TTL 过期 → decayed (不删, 与桌面 applyDecay 同精神) */
|
|
784
|
+
applyTtl(nowIso) {
|
|
785
|
+
this.ensureLoaded();
|
|
786
|
+
const now = Date.parse(nowIso || (/* @__PURE__ */ new Date()).toISOString());
|
|
787
|
+
let changed = false;
|
|
788
|
+
for (const e of this.entries) {
|
|
789
|
+
if (e.decayed || e.crystallized || !e.ttlMs) continue;
|
|
790
|
+
const base5 = Date.parse(e.lastRecalledAt || e.createdAt) || now;
|
|
791
|
+
if (now - base5 > e.ttlMs) {
|
|
792
|
+
e.decayed = true;
|
|
793
|
+
changed = true;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
if (changed) this.writeAll(this.entries);
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* 记忆迭代 (每轮循环后调用): 从「用户目标 + 工具结果 + 最终回复」提炼
|
|
800
|
+
* 新事实 / 偏好 / 关系。规则启发式 (零 LLM 成本): 信号词命中即存,
|
|
801
|
+
* 已经存过的同内容自动合并 (remember 幂等)。
|
|
802
|
+
*/
|
|
803
|
+
autoIterate(input) {
|
|
804
|
+
this.ensureLoaded();
|
|
805
|
+
const parts = [String(input.goal || ""), ...input.toolResults || [], String(input.reply || "")];
|
|
806
|
+
const text = parts.filter(Boolean).join("\n");
|
|
807
|
+
if (!text.trim()) return 0;
|
|
808
|
+
let added = 0;
|
|
809
|
+
const pref = text.match(/(?:我|用户|本人)(?:喜欢|偏好|热爱|想要|希望).{2,40}?/g);
|
|
810
|
+
if (pref) {
|
|
811
|
+
for (const m2 of pref.slice(0, 3)) {
|
|
812
|
+
const content = m2.replace(/^(?:我|用户|本人)/, "").trim();
|
|
813
|
+
if (validMemoryContent(content)) {
|
|
814
|
+
this.remember({ content: "\u504F\u597D: " + content, kind: "preference", source: input.source || "iterate" });
|
|
815
|
+
added++;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
const ban = text.match(/不要(?:再)?.{2,40}?|不要.{2,40}?|不能.{2,40}?|禁止.{2,40}?|记住.{2,40}?/g);
|
|
820
|
+
if (ban) {
|
|
821
|
+
for (const m2 of ban.slice(0, 2)) {
|
|
822
|
+
if (validMemoryContent(m2)) {
|
|
823
|
+
this.remember({ content: "\u7EA6\u5B9A: " + m2.trim(), kind: "insight", source: input.source || "iterate", importance: 0.6 });
|
|
824
|
+
added++;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
const relMatch = text.match(/([一-龥]{2,6})是(?:我的)?(家人|妈妈|爸爸|老婆|老公|朋友|同事|客户|老板|领导|兄弟|姐妹|妻子|丈夫|合作伙伴)/);
|
|
829
|
+
if (relMatch) {
|
|
830
|
+
const name10 = relMatch[1];
|
|
831
|
+
const kind = classifyRelation(relMatch[0], input.source || "");
|
|
832
|
+
const content = `${name10} \u662F${relMatch[2]}`;
|
|
833
|
+
this.remember({
|
|
834
|
+
content,
|
|
835
|
+
kind: "relationship",
|
|
836
|
+
source: input.source || "iterate",
|
|
837
|
+
importance: 0.8,
|
|
838
|
+
relation: { did: "", kind, trust: 0.6 }
|
|
839
|
+
});
|
|
840
|
+
added++;
|
|
841
|
+
}
|
|
842
|
+
return added;
|
|
843
|
+
}
|
|
844
|
+
/** 快照 (run 结束/重启恢复用) */
|
|
845
|
+
snapshot() {
|
|
846
|
+
this.ensureLoaded();
|
|
847
|
+
this.applyTtl();
|
|
848
|
+
return { version: 1, agentId: this.agentId, entries: [...this.entries], savedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
849
|
+
}
|
|
850
|
+
/** 从快照恢复 (bootstrap) — 合并, 不覆盖已有 */
|
|
851
|
+
restore(snap) {
|
|
852
|
+
if (!snap || !Array.isArray(snap.entries)) return;
|
|
853
|
+
this.ensureLoaded();
|
|
854
|
+
for (const e of snap.entries) {
|
|
855
|
+
const exists = this.entries.some((x) => x.id === e.id);
|
|
856
|
+
if (!exists) this.entries.push(e);
|
|
857
|
+
}
|
|
858
|
+
this.writeAll(this.entries);
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* 稳态记忆块: 固化优先 + 未淡化 + importance 降序, 始终注入 system。
|
|
862
|
+
* 与 recall(query) 互补: recall 是即时相关, stableBlock 是用户确认/多次命中的事实。
|
|
863
|
+
*/
|
|
864
|
+
stableBlock(maxEntries = 6) {
|
|
865
|
+
this.ensureLoaded();
|
|
866
|
+
this.applyTtl();
|
|
867
|
+
const picked = this.entries.filter((e) => !e.decayed).sort((a, b) => {
|
|
868
|
+
if (a.crystallized !== b.crystallized) return a.crystallized ? -1 : 1;
|
|
869
|
+
return b.importance - a.importance;
|
|
870
|
+
}).slice(0, maxEntries);
|
|
871
|
+
if (!picked.length) return "";
|
|
872
|
+
const lines = picked.map((e) => {
|
|
873
|
+
const tag = `${e.kind}${e.crystallized ? "\u2605" : ""}`;
|
|
874
|
+
return `- (${tag}, ${e.importance.toFixed(2)}) ${e.content.slice(0, 120)}`;
|
|
875
|
+
});
|
|
876
|
+
return `<stable-memory>
|
|
877
|
+
${lines.join("\n")}
|
|
878
|
+
</stable-memory>`;
|
|
879
|
+
}
|
|
880
|
+
/** 记忆指纹: 摘要 hash (参与上下文缓存 key; 记忆变了 → 指纹变 → 缓存 miss) */
|
|
881
|
+
fingerprint() {
|
|
882
|
+
this.ensureLoaded();
|
|
883
|
+
this.applyTtl();
|
|
884
|
+
const active = this.entries.filter((e) => !e.decayed).sort((a, b) => a.id.localeCompare(b.id));
|
|
885
|
+
const digest3 = active.slice(-30).map((e) => `${e.kind}:${e.content.slice(0, 60)}:${e.importance.toFixed(2)}`).join("|");
|
|
886
|
+
return quickHash(digest3);
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
MobileCache = class {
|
|
890
|
+
prefix;
|
|
891
|
+
constructor(prefix = "bolloon_mobile_cache") {
|
|
892
|
+
this.prefix = prefix;
|
|
893
|
+
}
|
|
894
|
+
get(key, ttlMs = 6e4) {
|
|
895
|
+
try {
|
|
896
|
+
const raw = typeof localStorage !== "undefined" ? localStorage.getItem(`${this.prefix}_${key}`) : null;
|
|
897
|
+
if (!raw) return null;
|
|
898
|
+
const parsed = JSON.parse(raw);
|
|
899
|
+
if (!parsed || !parsed.v || typeof parsed.at !== "number") return null;
|
|
900
|
+
if (Date.now() - parsed.at > ttlMs) return null;
|
|
901
|
+
return parsed.v;
|
|
902
|
+
} catch {
|
|
903
|
+
return null;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
set(key, value2) {
|
|
907
|
+
try {
|
|
908
|
+
if (typeof localStorage !== "undefined") localStorage.setItem(`${this.prefix}_${key}`, JSON.stringify({ v: value2, at: Date.now() }));
|
|
909
|
+
} catch {
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
clear() {
|
|
913
|
+
try {
|
|
914
|
+
const keys = [];
|
|
915
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
916
|
+
const k = localStorage.key(i);
|
|
917
|
+
if (k && k.startsWith(this.prefix)) keys.push(k);
|
|
918
|
+
}
|
|
919
|
+
for (const k of keys) localStorage.removeItem(k);
|
|
920
|
+
} catch {
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
|
|
626
927
|
// node_modules/@diap/sdk/dist/types/key.js
|
|
627
928
|
var init_key = __esm({
|
|
628
929
|
"node_modules/@diap/sdk/dist/types/key.js"() {
|
|
@@ -89730,8 +90031,10 @@ zoo`.split("\n");
|
|
|
89730
90031
|
var mobile_agent_exports = {};
|
|
89731
90032
|
__export(mobile_agent_exports, {
|
|
89732
90033
|
MOBILE_JOIN_DOC_RE: () => MOBILE_JOIN_DOC_RE,
|
|
90034
|
+
adaptiveTimeoutMs: () => adaptiveTimeoutMs,
|
|
89733
90035
|
callRemoteAgent: () => callRemoteAgent,
|
|
89734
90036
|
cancelPhoneAgent: () => cancelPhoneAgent,
|
|
90037
|
+
classifyMobileError: () => classifyMobileError,
|
|
89735
90038
|
default: () => mobile_agent_default,
|
|
89736
90039
|
detectJoinDocUrl: () => detectJoinDocUrl,
|
|
89737
90040
|
ensureIdentity: () => ensureIdentity,
|
|
@@ -89742,7 +90045,9 @@ zoo`.split("\n");
|
|
|
89742
90045
|
handleIncomingAgentMessage: () => handleIncomingAgentMessage,
|
|
89743
90046
|
handleIncomingPhoneMessage: () => handleIncomingPhoneMessage,
|
|
89744
90047
|
identityStatus: () => identityStatus,
|
|
90048
|
+
isConfirmRequiredTool: () => isConfirmRequiredTool,
|
|
89745
90049
|
joinGatewayFromDoc: () => joinGatewayFromDoc,
|
|
90050
|
+
listMobileRuns: () => listMobileRuns,
|
|
89746
90051
|
loginIdentity: () => loginIdentity,
|
|
89747
90052
|
logoutIdentity: () => logoutIdentity,
|
|
89748
90053
|
notifyAgentReply: () => notifyAgentReply,
|
|
@@ -89756,6 +90061,7 @@ zoo`.split("\n");
|
|
|
89756
90061
|
setAgentTransport: () => setAgentTransport,
|
|
89757
90062
|
setLlmConfig: () => setLlmConfig,
|
|
89758
90063
|
signWithIdentity: () => signWithIdentity,
|
|
90064
|
+
toolFingerprint: () => toolFingerprint,
|
|
89759
90065
|
updateIdentityProfile: () => updateIdentityProfile,
|
|
89760
90066
|
verifyIdentitySignature: () => verifyIdentitySignature
|
|
89761
90067
|
});
|
|
@@ -90145,7 +90451,7 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
|
|
|
90145
90451
|
try {
|
|
90146
90452
|
const llm = getLlmConfig2();
|
|
90147
90453
|
if (llm && llm.baseUrl) {
|
|
90148
|
-
const reply = await runWebAgentLoop(goal, { baseUrl: llm.baseUrl, apiKey: llm.apiKey, model: llm.model, maxTokens: llm.maxTokens });
|
|
90454
|
+
const reply = await runWebAgentLoop(goal, { baseUrl: llm.baseUrl, apiKey: llm.apiKey, model: llm.model, maxTokens: llm.maxTokens }, { silentDegrade: true });
|
|
90149
90455
|
if (reply) return reply;
|
|
90150
90456
|
}
|
|
90151
90457
|
} catch {
|
|
@@ -90180,36 +90486,105 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
|
|
|
90180
90486
|
}
|
|
90181
90487
|
return `\u5DF2\u6536\u5230: "${(goal || "").slice(0, 40)}"\u3002\u8FD9\u662F\u624B\u673A\u7AEF Agent \u529F\u80FD\u5C42\u7684\u672C\u5730\u6267\u884C (\u6570\u636E\u540C\u6B65\u4E0E agent \u529F\u80FD\u5DF2\u5206\u79BB)\u3002`;
|
|
90182
90488
|
}
|
|
90183
|
-
function
|
|
90489
|
+
function listMobileRuns(did, limit = 20) {
|
|
90490
|
+
try {
|
|
90491
|
+
const raw = typeof localStorage !== "undefined" ? localStorage.getItem(runsKey(did)) : null;
|
|
90492
|
+
if (!raw) return [];
|
|
90493
|
+
const parsed = JSON.parse(raw);
|
|
90494
|
+
return Array.isArray(parsed) ? parsed.slice(0, limit) : [];
|
|
90495
|
+
} catch {
|
|
90496
|
+
return [];
|
|
90497
|
+
}
|
|
90498
|
+
}
|
|
90499
|
+
function saveRunRecord(did, rec) {
|
|
90500
|
+
try {
|
|
90501
|
+
const prev = listMobileRuns(did, 50);
|
|
90502
|
+
const next = [rec, ...prev.filter((r) => r.runId !== rec.runId)].slice(0, 50);
|
|
90503
|
+
if (typeof localStorage !== "undefined") localStorage.setItem(runsKey(did), JSON.stringify(next));
|
|
90504
|
+
} catch {
|
|
90505
|
+
}
|
|
90506
|
+
}
|
|
90507
|
+
function toolFingerprint() {
|
|
90508
|
+
if (!_toolFingerprint) _toolFingerprint = quickHash("tools|" + toolNames());
|
|
90509
|
+
return _toolFingerprint;
|
|
90510
|
+
}
|
|
90511
|
+
function isConfirmRequiredTool(tool) {
|
|
90512
|
+
return CONFIRM_REQUIRED_TOOLS.includes(tool);
|
|
90513
|
+
}
|
|
90514
|
+
function buildAgentSystem(opts = {}) {
|
|
90184
90515
|
const persona = loadPersona();
|
|
90185
90516
|
const personaBlock = buildPersonaPrompt(persona);
|
|
90517
|
+
const userBlock = opts.userDoc && opts.userDoc.trim() ? `\u3010\u7528\u6237\u6863\u6848\u3011
|
|
90518
|
+
${opts.userDoc.trim().slice(0, 400)}` : "";
|
|
90519
|
+
const memoryBlock = opts.memoryBlock && opts.memoryBlock.trim() ? `\u3010\u8BB0\u5FC6\u3011
|
|
90520
|
+
${opts.memoryBlock.trim().slice(0, 600)}` : "";
|
|
90521
|
+
const stableBlock = opts.memory && opts.memory.stableBlock ? opts.memory.stableBlock(6) : "";
|
|
90522
|
+
const progressBlock = opts.progress && opts.progress.length ? `\u3010\u672C\u8F6E\u8FDB\u5EA6\u3011
|
|
90523
|
+
\u5DF2\u5B8C\u6210: ${opts.progress.slice(-8).map((s2, i) => `${i + 1}. ${s2.slice(0, 80)}`).join("\n")}` : "";
|
|
90524
|
+
const planHint = opts.plan && opts.plan.trim() ? `\u3010\u8BA1\u5212\u3011
|
|
90525
|
+
${opts.plan.trim().slice(0, 300)}` : "\u4EFB\u52A1\u590D\u6742\u65F6\u5148\u5728\u5185\u5FC3\u62C6\u6B65\u9AA4, \u9010\u6B65\u6267\u884C, \u6BCF\u6B65\u7528\u4E00\u4E2A\u5DE5\u5177\u63A8\u8FDB\u3002";
|
|
90526
|
+
const channelBlock = opts.channel && opts.channel !== "text" ? `\u3010\u8F93\u5165\u901A\u9053\u3011
|
|
90527
|
+
\u672C\u6B21\u8F93\u5165\u662F${CHANNEL_LABEL[opts.channel]}\u3002\u4F60\u80FD\u7406\u89E3${CHANNEL_LABEL[opts.channel]}\u5185\u5BB9\u5E76\u636E\u6B64\u56DE\u7B54/\u6267\u884C\u3002` : "";
|
|
90186
90528
|
return [
|
|
90187
90529
|
"\u4F60\u662F\u624B\u673A\u7AEF Bolloon \u667A\u80FD\u4F53 (\u81EA\u6CBB\u8282\u70B9), \u7528\u4E2D\u6587\u7B80\u6D01\u56DE\u590D\u3002",
|
|
90188
90530
|
personaBlock,
|
|
90531
|
+
userBlock,
|
|
90532
|
+
memoryBlock,
|
|
90533
|
+
stableBlock,
|
|
90189
90534
|
'\u4F60\u6709\u4EE5\u4E0B\u5DE5\u5177, \u6309\u9700\u8C03\u7528 (JSON: {"tool":"\u540D\u5B57","args":{...}}), \u4E0D\u9700\u8981\u5DE5\u5177\u5C31\u76F4\u63A5\u56DE\u7B54:',
|
|
90190
|
-
"- get_status: \u67E5\u672C\u673A\u8EAB\u4EFD/\u5165\u7F51/\u94B1\u5305
|
|
90535
|
+
"- get_status: \u67E5\u672C\u673A\u8EAB\u4EFD/\u5165\u7F51/\u94B1\u5305/\u8FDE\u63A5\u72B6\u6001 (args: \u65E0)",
|
|
90191
90536
|
"- get_wallet: \u67E5\u94B1\u5305\u4F59\u989D (args: \u65E0)",
|
|
90192
|
-
"- get_identity: \u67E5\u672C\u673A
|
|
90537
|
+
"- get_identity: \u67E5\u672C\u673A\u8EAB\u4EFD\u8BC1\u660E (args: \u65E0)",
|
|
90193
90538
|
"- get_contacts: \u67E5\u8054\u7CFB\u65B9\u5F0F\u6388\u6743\u72B6\u6001 (args: \u65E0)",
|
|
90194
|
-
"\u6BCF\u8F6E\
|
|
90195
|
-
|
|
90539
|
+
"\u6BCF\u8F6E\u6700\u591A\u7528\u4E00\u4E2A\u5DE5\u5177; \u62FF\u5230\u7ED3\u679C\u540E\u7EE7\u7EED\u601D\u8003, \u76F4\u5230\u4EFB\u52A1\u5B8C\u6210\u7ED9\u51FA\u6700\u7EC8\u56DE\u7B54\u3002",
|
|
90540
|
+
progressBlock,
|
|
90541
|
+
planHint,
|
|
90542
|
+
channelBlock,
|
|
90543
|
+
"\u80FD\u52A8\u6027: \u5148\u52A8\u624B\u522B\u5148\u95EE; \u505A\u5230\u5E95 (\u4EA4\u4ED8=\u80FD\u7528\u7684\u7ED3\u679C); \u4E00\u4E2A\u5DE5\u5177\u4E0D\u591F\u5C31\u6362\u65B9\u5F0F\u7EE7\u7EED; \u88AB\u963B\u585E\u5982\u5B9E\u8BF4, \u4E0D\u7F16\u9020\u3002"
|
|
90544
|
+
].filter(Boolean).join("\n");
|
|
90545
|
+
}
|
|
90546
|
+
function classifyMobileError(err2) {
|
|
90547
|
+
const msg = String(err2?.message || err2?.cause?.message || err2 || "");
|
|
90548
|
+
if (/timeout|timed out|AbortError|aborted/i.test(msg)) return { cls: "timeout", retryable: true, hint: "\u8BF7\u6C42\u8D85\u65F6, \u7A0D\u540E\u91CD\u8BD5" };
|
|
90549
|
+
if (/terminated|ECONNRESET|socket hang up|fetch failed|network|ETIMEDOUT|ECONNREFUSED|UND_ERR|Failed to fetch/i.test(msg)) return { cls: "network", retryable: true, hint: "\u7F51\u7EDC\u6CE2\u52A8, \u6B63\u5728\u91CD\u8BD5" };
|
|
90550
|
+
if (/401|403|quota|rate limit|API key|invalid api|HTTP 40\d|HTTP 429/i.test(msg)) return { cls: "api", retryable: false, hint: "\u63A5\u53E3/\u914D\u989D\u9519\u8BEF, \u65E0\u6CD5\u81EA\u52A8\u6062\u590D" };
|
|
90551
|
+
return { cls: "unknown", retryable: true, hint: "\u672A\u77E5\u9519\u8BEF, \u5C1D\u8BD5\u7EE7\u7EED" };
|
|
90552
|
+
}
|
|
90553
|
+
function adaptiveTimeoutMs(ctxChars, baseMs = 1e4) {
|
|
90554
|
+
const byLen = Math.ceil(Math.max(0, ctxChars) / 1024) * 4e3 + baseMs;
|
|
90555
|
+
return Math.max(baseMs, byLen);
|
|
90196
90556
|
}
|
|
90197
90557
|
async function webAgentLlmCall(messages2, llm) {
|
|
90198
|
-
const
|
|
90199
|
-
|
|
90200
|
-
|
|
90201
|
-
|
|
90202
|
-
|
|
90203
|
-
|
|
90204
|
-
|
|
90205
|
-
|
|
90206
|
-
|
|
90207
|
-
|
|
90208
|
-
|
|
90209
|
-
|
|
90210
|
-
|
|
90211
|
-
|
|
90212
|
-
|
|
90558
|
+
const ctxChars = messages2.reduce((n, m2) => n + String(m2.content || "").length, 0);
|
|
90559
|
+
const timeoutMs = adaptiveTimeoutMs(ctxChars);
|
|
90560
|
+
let lastErr = null;
|
|
90561
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
90562
|
+
try {
|
|
90563
|
+
const resp = await fetch(llm.baseUrl.replace(/\/$/, "") + "/chat/completions", {
|
|
90564
|
+
method: "POST",
|
|
90565
|
+
headers: { "Content-Type": "application/json", "Authorization": "Bearer " + (llm.apiKey || "") },
|
|
90566
|
+
body: JSON.stringify({
|
|
90567
|
+
model: llm.model || "glm-5.3",
|
|
90568
|
+
messages: messages2,
|
|
90569
|
+
max_tokens: llm.maxTokens || 2048,
|
|
90570
|
+
temperature: 0.4
|
|
90571
|
+
}),
|
|
90572
|
+
// eslint-disable-next-line no-undef
|
|
90573
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
90574
|
+
});
|
|
90575
|
+
if (!resp.ok) throw new Error("LLM HTTP " + resp.status + ": " + (await resp.text()).slice(0, 120));
|
|
90576
|
+
const data = await resp.json();
|
|
90577
|
+
const content = data?.choices?.[0]?.message?.content;
|
|
90578
|
+
if (!content) throw new Error("LLM \u7A7A\u56DE\u590D");
|
|
90579
|
+
return String(content).trim();
|
|
90580
|
+
} catch (e) {
|
|
90581
|
+
const cls = classifyMobileError(e);
|
|
90582
|
+
lastErr = e;
|
|
90583
|
+
if (cls.cls === "timeout" || cls.cls === "api" || attempt >= 2) throw e;
|
|
90584
|
+
await new Promise((r) => setTimeout(r, attempt === 0 ? 500 : 1500));
|
|
90585
|
+
}
|
|
90586
|
+
}
|
|
90587
|
+
throw lastErr || new Error("LLM \u8C03\u7528\u5931\u8D25");
|
|
90213
90588
|
}
|
|
90214
90589
|
function extractToolCall(raw) {
|
|
90215
90590
|
if (!raw) return null;
|
|
@@ -90266,29 +90641,124 @@ ${r.steps.map((s3) => `${s3.ok ? "\u2713" : "\u2717"} ${s3.step}: ${s3.note}`).j
|
|
|
90266
90641
|
}
|
|
90267
90642
|
throw new Error("unbalanced");
|
|
90268
90643
|
}
|
|
90269
|
-
async function runWebAgentLoop(goal, llm) {
|
|
90644
|
+
async function runWebAgentLoop(goal, llm, opts = {}) {
|
|
90645
|
+
const id = await ensureIdentity();
|
|
90646
|
+
const did = id.did;
|
|
90647
|
+
const memory = opts.memory || new MobileMemory(did);
|
|
90648
|
+
memory.restore(memory.snapshot());
|
|
90649
|
+
const channel = opts.channel || "text";
|
|
90650
|
+
const runId = quickHash(did + "|" + goal + "|" + Date.now());
|
|
90651
|
+
const record = { runId, goal: String(goal || "").slice(0, 80), status: "running", steps: 0, startedAt: (/* @__PURE__ */ new Date()).toISOString(), channel };
|
|
90652
|
+
saveRunRecord(did, record);
|
|
90653
|
+
const userDoc = opts.userDoc ?? [id.name ? `\u540D\u5B57: ${id.name}` : "", id.phone ? `\u624B\u673A\u53F7: ${id.phone}` : "", id.email ? `\u90AE\u7BB1: ${id.email}` : "", id.note ? `\u5907\u6CE8: ${id.note}` : ""].filter(Boolean).join("\n");
|
|
90654
|
+
const memoryBlock = memory.recall(String(goal || ""), { limit: 3 }).map((e) => e.content).join("\n");
|
|
90655
|
+
const sysCache = new MobileCache("bolloon_sys");
|
|
90656
|
+
const stableFp = memory.stableBlock(6);
|
|
90657
|
+
const sysFp = quickHash([
|
|
90658
|
+
userDoc,
|
|
90659
|
+
opts.soulDoc || "soul-default",
|
|
90660
|
+
memoryBlock,
|
|
90661
|
+
stableFp,
|
|
90662
|
+
toolFingerprint(),
|
|
90663
|
+
channel,
|
|
90664
|
+
opts.plan || ""
|
|
90665
|
+
].join("|"));
|
|
90270
90666
|
const messages2 = [
|
|
90271
|
-
{ role: "system", content: buildAgentSystem() },
|
|
90272
|
-
{ role: "user", content: goal }
|
|
90667
|
+
{ role: "system", content: sysCache.get(sysFp, 10 * 6e4) || buildAgentSystem({ goal, memoryBlock, userDoc, soulDoc: opts.soulDoc, channel, plan: opts.plan, memory }) },
|
|
90668
|
+
{ role: "user", content: String(goal || "") }
|
|
90273
90669
|
];
|
|
90274
|
-
|
|
90670
|
+
sysCache.set(sysFp, messages2[0].content);
|
|
90671
|
+
const MAX_STEPS = 150;
|
|
90672
|
+
const NO_PROGRESS_LIMIT = 10;
|
|
90673
|
+
const COMPACT_AT = 30;
|
|
90275
90674
|
const steps = [];
|
|
90675
|
+
let lastToolSig = "";
|
|
90676
|
+
let noProgress = 0;
|
|
90276
90677
|
for (let i = 0; i < MAX_STEPS; i++) {
|
|
90277
|
-
|
|
90678
|
+
if (opts.signal?.aborted) {
|
|
90679
|
+
record.status = "cancelled";
|
|
90680
|
+
record.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
90681
|
+
saveRunRecord(did, record);
|
|
90682
|
+
_lastWorklog = [...steps, "\u26A0 \u5DF2\u53D6\u6D88"];
|
|
90683
|
+
return `[\u624B\u673A\u81EA\u6CBB\u6267\u884C ${steps.length} \u6B65 \xB7 \u5DF2\u53D6\u6D88]`;
|
|
90684
|
+
}
|
|
90685
|
+
let raw;
|
|
90686
|
+
try {
|
|
90687
|
+
raw = await webAgentLlmCall(messages2, llm);
|
|
90688
|
+
} catch (e) {
|
|
90689
|
+
const hint = classifyMobileError(e).hint;
|
|
90690
|
+
record.status = "error";
|
|
90691
|
+
record.error = String(e?.message || e).slice(0, 120);
|
|
90692
|
+
record.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
90693
|
+
saveRunRecord(did, record);
|
|
90694
|
+
_lastWorklog = [...steps, `\u26A0 ${hint}`];
|
|
90695
|
+
if (!steps.length && opts.silentDegrade) throw e;
|
|
90696
|
+
if (!steps.length) return `(${hint}, \u8BF7\u7A0D\u540E\u91CD\u8BD5)`;
|
|
90697
|
+
return `[\u624B\u673A\u81EA\u6CBB\u6267\u884C ${steps.length} \u6B65 \xB7 ${hint}]
|
|
90698
|
+
\u5DF2\u5B8C\u6210\u7684\u6B65\u9AA4\u7ED3\u679C\u5982\u4E0B:
|
|
90699
|
+
${steps.join("\n")}
|
|
90700
|
+
(${hint}, \u8BF7\u7A0D\u540E\u91CD\u8BD5)`;
|
|
90701
|
+
}
|
|
90278
90702
|
const toolJson = extractToolCall(raw);
|
|
90279
90703
|
if (toolJson && typeof toolJson.tool === "string" && WEB_AGENT_TOOLS[toolJson.tool]) {
|
|
90280
|
-
const
|
|
90704
|
+
const sig = toolJson.tool + "|" + JSON.stringify(toolJson.args || {});
|
|
90705
|
+
noProgress = sig === lastToolSig ? noProgress + 1 : 0;
|
|
90706
|
+
lastToolSig = sig;
|
|
90707
|
+
if (noProgress >= NO_PROGRESS_LIMIT) {
|
|
90708
|
+
record.status = "done";
|
|
90709
|
+
record.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
90710
|
+
saveRunRecord(did, record);
|
|
90711
|
+
_lastWorklog = [...steps, `\u26A0 \u8FDE\u7EED ${NO_PROGRESS_LIMIT} \u8F6E\u91CD\u590D\u540C\u4E00\u5DE5\u5177\u8C03\u7528 (${toolJson.tool}), \u5224\u65AD\u6B7B\u5FAA\u73AF\u63D0\u524D\u505C\u6B62`];
|
|
90712
|
+
return `[\u624B\u673A\u81EA\u6CBB\u6267\u884C ${steps.length} \u6B65 \xB7 \u68C0\u6D4B\u5230\u91CD\u590D\u52A8\u4F5C\u63D0\u524D\u505C\u6B62]
|
|
90713
|
+
` + raw.slice(0, 200);
|
|
90714
|
+
}
|
|
90715
|
+
if (isConfirmRequiredTool(toolJson.tool)) {
|
|
90716
|
+
const allow = opts.confirm ? await opts.confirm(toolJson.tool, toolJson.args) : false;
|
|
90717
|
+
if (!allow) {
|
|
90718
|
+
const denied = `\u5DE5\u5177 ${toolJson.tool} \u9700\u8981\u786E\u8BA4, \u672C\u6B21\u672A\u6267\u884C (\u8BF7\u7528\u6237\u6279\u51C6\u540E\u91CD\u8BD5)\u3002`;
|
|
90719
|
+
steps.push(`\u{1F512} ${toolJson.tool} \u2192 ${denied}`);
|
|
90720
|
+
messages2.push({ role: "assistant", content: raw });
|
|
90721
|
+
messages2.push({ role: "user", content: `\u5DE5\u5177\u7ED3\u679C: ${denied}` });
|
|
90722
|
+
continue;
|
|
90723
|
+
}
|
|
90724
|
+
}
|
|
90725
|
+
const out = await WEB_AGENT_TOOLS[toolJson.tool](toolJson.args || {});
|
|
90281
90726
|
steps.push(`\u{1F527} ${toolJson.tool} \u2192 ${out.slice(0, 80)}`);
|
|
90727
|
+
record.steps = steps.length;
|
|
90728
|
+
saveRunRecord(did, record);
|
|
90729
|
+
if (opts.onProgress) {
|
|
90730
|
+
try {
|
|
90731
|
+
opts.onProgress({ step: steps.length, tool: toolJson.tool, output: out.slice(0, 120) });
|
|
90732
|
+
} catch {
|
|
90733
|
+
}
|
|
90734
|
+
}
|
|
90735
|
+
if (steps.length > 60) steps.splice(0, steps.length - 60);
|
|
90282
90736
|
messages2.push({ role: "assistant", content: raw });
|
|
90283
90737
|
messages2.push({ role: "user", content: `\u5DE5\u5177\u7ED3\u679C: ${out}` });
|
|
90738
|
+
if (messages2.length > COMPACT_AT) {
|
|
90739
|
+
const keep = messages2.slice(0, 2);
|
|
90740
|
+
const recent = messages2.slice(-10);
|
|
90741
|
+
const summary = `\uFF08\u4E2D\u95F4 ${messages2.length - 12} \u8F6E\u5BF9\u8BDD\u5DF2\u538B\u7F29\uFF09\u5DF2\u5B8C\u6210\u6B65\u9AA4: ${steps.slice(-12).map((s2) => s2.slice(0, 60)).join(" | ")}`;
|
|
90742
|
+
keep.push({ role: "user", content: summary });
|
|
90743
|
+
messages2.length = 0;
|
|
90744
|
+
messages2.push(...keep, ...recent);
|
|
90745
|
+
}
|
|
90284
90746
|
continue;
|
|
90285
90747
|
}
|
|
90748
|
+
memory.autoIterate({ goal: String(goal || ""), toolResults: steps, reply: raw, source: "session" });
|
|
90749
|
+
memory.snapshot();
|
|
90750
|
+
record.status = "done";
|
|
90751
|
+
record.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
90752
|
+
saveRunRecord(did, record);
|
|
90286
90753
|
_lastWorklog = steps.length ? [...steps, `\u{1F4AC} ${raw.slice(0, 60)}`] : [`\u{1F4AC} ${raw.slice(0, 60)}`];
|
|
90287
90754
|
if (steps.length) return `[\u624B\u673A\u81EA\u6CBB\u6267\u884C ${steps.length} \u6B65]
|
|
90288
90755
|
` + raw;
|
|
90289
90756
|
return raw;
|
|
90290
90757
|
}
|
|
90291
90758
|
const last = messages2[messages2.length - 1]?.content || "";
|
|
90759
|
+
record.status = "done";
|
|
90760
|
+
record.finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
90761
|
+
saveRunRecord(did, record);
|
|
90292
90762
|
_lastWorklog = [...steps, "\u26A0 \u8FBE\u5230\u6B65\u6570\u4E0A\u9650, \u5DF2\u505C\u6B62"];
|
|
90293
90763
|
return `[\u624B\u673A\u81EA\u6CBB\u6267\u884C, \u8FBE\u5230 ${MAX_STEPS} \u6B65\u4E0A\u9650]
|
|
90294
90764
|
${last.slice(0, 200)}`;
|
|
@@ -90510,12 +90980,13 @@ ${last.slice(0, 200)}`;
|
|
|
90510
90980
|
} catch {
|
|
90511
90981
|
}
|
|
90512
90982
|
}
|
|
90513
|
-
var IDENTITY_DB, _identity, _keyMgr, _ed25519, _identityDb, BUILTIN_LLM, _llmConfig, _lastWorklog, MOBILE_JOIN_DOC_RE, MOBILE_JOIN_STATE_KEY, WEB_AGENT_TOOLS, _send2, _ownDid, replyHandlers, inboundChatHandlers, mobile_agent_default;
|
|
90983
|
+
var IDENTITY_DB, _identity, _keyMgr, _ed25519, _identityDb, BUILTIN_LLM, _llmConfig, _lastWorklog, MOBILE_JOIN_DOC_RE, MOBILE_JOIN_STATE_KEY, runsKey, toolNames, _toolFingerprint, CHANNEL_LABEL, CONFIRM_REQUIRED_TOOLS, WEB_AGENT_TOOLS, _send2, _ownDid, replyHandlers, inboundChatHandlers, mobile_agent_default;
|
|
90514
90984
|
var init_mobile_agent = __esm({
|
|
90515
90985
|
"src/web/mobile-agent.ts"() {
|
|
90516
90986
|
"use strict";
|
|
90517
90987
|
init_mobile_gateway();
|
|
90518
90988
|
init_mobile_persona();
|
|
90989
|
+
init_mobile_memory();
|
|
90519
90990
|
IDENTITY_DB = "bolloon-mobile";
|
|
90520
90991
|
_identity = null;
|
|
90521
90992
|
_keyMgr = null;
|
|
@@ -90531,11 +91002,21 @@ ${last.slice(0, 200)}`;
|
|
|
90531
91002
|
_lastWorklog = [];
|
|
90532
91003
|
MOBILE_JOIN_DOC_RE = /read\s+(https?:\/\/\S*bolloon-gateway-join\.md)/i;
|
|
90533
91004
|
MOBILE_JOIN_STATE_KEY = "bolloon_gateway_join";
|
|
91005
|
+
runsKey = (did) => `bolloon_mobile_runs_${did}`;
|
|
91006
|
+
toolNames = () => Object.keys(WEB_AGENT_TOOLS).sort().join(",");
|
|
91007
|
+
_toolFingerprint = "";
|
|
91008
|
+
CHANNEL_LABEL = {
|
|
91009
|
+
text: "\u6587\u5B57",
|
|
91010
|
+
image: "\u56FE\u7247",
|
|
91011
|
+
audio: "\u8BED\u97F3",
|
|
91012
|
+
file: "\u6587\u4EF6"
|
|
91013
|
+
};
|
|
91014
|
+
CONFIRM_REQUIRED_TOOLS = [];
|
|
90534
91015
|
WEB_AGENT_TOOLS = {
|
|
90535
91016
|
get_status: async () => {
|
|
90536
91017
|
try {
|
|
90537
91018
|
const id = await ensureIdentity();
|
|
90538
|
-
return
|
|
91019
|
+
return `\u8EAB\u4EFD: ${id.did.slice(0, 16)}... \u540D\u5B57: ${id.name}`;
|
|
90539
91020
|
} catch (e) {
|
|
90540
91021
|
return "\u8EAB\u4EFD\u8BFB\u53D6\u5931\u8D25: " + String(e?.message || e).slice(0, 60);
|
|
90541
91022
|
}
|
|
@@ -90562,7 +91043,7 @@ ${last.slice(0, 200)}`;
|
|
|
90562
91043
|
},
|
|
90563
91044
|
get_identity: async () => {
|
|
90564
91045
|
const id = await ensureIdentity();
|
|
90565
|
-
return
|
|
91046
|
+
return `\u8EAB\u4EFD\u8BC1\u660E: ${id.did} \u540D\u5B57: ${id.name}`;
|
|
90566
91047
|
},
|
|
90567
91048
|
get_contacts: async () => {
|
|
90568
91049
|
try {
|
|
@@ -169488,6 +169969,26 @@ ${joined}`);
|
|
|
169488
169969
|
await agentLayer.handleIncomingPhoneMessage(type, msgBody, fromPeer);
|
|
169489
169970
|
return;
|
|
169490
169971
|
}
|
|
169972
|
+
if (type === "world.card") {
|
|
169973
|
+
try {
|
|
169974
|
+
const card = JSON.parse(msgBody);
|
|
169975
|
+
await core.world.pushNearbyCard(card, fromPeer);
|
|
169976
|
+
} catch {
|
|
169977
|
+
}
|
|
169978
|
+
return;
|
|
169979
|
+
}
|
|
169980
|
+
if (type === "world.card.request") {
|
|
169981
|
+
try {
|
|
169982
|
+
const feed = await core.world.cardFeed();
|
|
169983
|
+
const first2 = feed?.opportunities?.[0];
|
|
169984
|
+
if (first2) {
|
|
169985
|
+
const { sendMobileP2PMessage: sendMobileP2PMessage2 } = await Promise.resolve().then(() => (init_mobile_p2p(), mobile_p2p_exports));
|
|
169986
|
+
await sendMobileP2PMessage2(fromPeer, "world.card", JSON.stringify({ ...first2, fromOwn: true }), "");
|
|
169987
|
+
}
|
|
169988
|
+
} catch {
|
|
169989
|
+
}
|
|
169990
|
+
return;
|
|
169991
|
+
}
|
|
169491
169992
|
} catch {
|
|
169492
169993
|
}
|
|
169493
169994
|
}
|
|
@@ -169571,6 +170072,8 @@ ${joined}`);
|
|
|
169571
170072
|
if (p.startsWith("/api/opportunities?")) return () => core.world.opportunities(p.slice("/api/opportunities?".length));
|
|
169572
170073
|
if (p === "/api/intents") return () => core.world.intents();
|
|
169573
170074
|
if (p === "/api/world/search" || p.startsWith("/api/world/search?")) return () => core.world.search("");
|
|
170075
|
+
if (p === "/api/world/cards") return () => core.world.cardFeed();
|
|
170076
|
+
if (p === "/api/world/cards/request") return () => core.world.requestNearbyCards();
|
|
169574
170077
|
if (p === "/api/orbit/status") return () => core.orbit.status();
|
|
169575
170078
|
if (p === "/api/orbit/replica") return () => core.orbit.replica();
|
|
169576
170079
|
if (p.startsWith("/sessions/")) {
|
|
@@ -169602,6 +170105,30 @@ ${joined}`);
|
|
|
169602
170105
|
const b = body || {};
|
|
169603
170106
|
return () => core.identity.login(String(b.name || ""));
|
|
169604
170107
|
}
|
|
170108
|
+
if (p === "/api/phone/directory/publish") {
|
|
170109
|
+
const b = body || {};
|
|
170110
|
+
return () => core.directory.publish(String(b.phone || ""), b.displayName ? String(b.displayName) : void 0);
|
|
170111
|
+
}
|
|
170112
|
+
if (p === "/api/phone/directory/lookup") {
|
|
170113
|
+
const b = body || {};
|
|
170114
|
+
return () => core.directory.lookup(String(b.phone || ""));
|
|
170115
|
+
}
|
|
170116
|
+
if (p === "/api/phone/directory/add-friend") {
|
|
170117
|
+
const b = body || {};
|
|
170118
|
+
return () => core.directory.addFriend(String(b.phone || ""), b.name ? String(b.name) : void 0, b.message ? String(b.message) : void 0);
|
|
170119
|
+
}
|
|
170120
|
+
if (p === "/api/phone/auth/bind") {
|
|
170121
|
+
const b = body || {};
|
|
170122
|
+
return () => core.phoneAuth.bind(String(b.phone || ""));
|
|
170123
|
+
}
|
|
170124
|
+
if (p === "/api/phone/auth/verify") {
|
|
170125
|
+
const b = body || {};
|
|
170126
|
+
return () => core.phoneAuth.verify(String(b.contactId || ""), String(b.challengeId || ""), String(b.code || ""));
|
|
170127
|
+
}
|
|
170128
|
+
if (p === "/api/phone/profile/phone") {
|
|
170129
|
+
const b = body || {};
|
|
170130
|
+
return () => core.identity.updateProfile({ phone: String(b.phone || "") });
|
|
170131
|
+
}
|
|
169605
170132
|
if (p === "/api/wallet/export") {
|
|
169606
170133
|
const b = body || {};
|
|
169607
170134
|
return () => core.wallet.export(String(b.id || ""));
|
|
@@ -169883,6 +170410,8 @@ ${joined}`);
|
|
|
169883
170410
|
dataLayer.requestLlmConfigFromPeer(pid).catch(() => {
|
|
169884
170411
|
});
|
|
169885
170412
|
});
|
|
170413
|
+
core.world.requestNearbyCards().catch(() => {
|
|
170414
|
+
});
|
|
169886
170415
|
return getMobileP2PState2();
|
|
169887
170416
|
} catch (e) {
|
|
169888
170417
|
return { connected: false, peerCount: 0, peerIds: [], error: String(e?.message || e).slice(0, 100) };
|
|
@@ -170015,6 +170544,10 @@ ${joined}`);
|
|
|
170015
170544
|
async logout() {
|
|
170016
170545
|
const a = await Promise.resolve().then(() => (init_mobile_agent(), mobile_agent_exports));
|
|
170017
170546
|
return a.logoutIdentity();
|
|
170547
|
+
},
|
|
170548
|
+
async updateProfile(partial) {
|
|
170549
|
+
const a = await Promise.resolve().then(() => (init_mobile_agent(), mobile_agent_exports));
|
|
170550
|
+
return a.updateIdentityProfile(partial || {});
|
|
170018
170551
|
}
|
|
170019
170552
|
},
|
|
170020
170553
|
peers: {
|
|
@@ -170249,6 +170782,103 @@ ${joined}`);
|
|
|
170249
170782
|
} catch {
|
|
170250
170783
|
return { ok: false, added: 0, results: [], note: "desktop-unreachable" };
|
|
170251
170784
|
}
|
|
170785
|
+
},
|
|
170786
|
+
// ── 首页卡片动态传送 (2026-10-16, leo: 卡片根据「附近 + 手机号通讯录」传送) ──
|
|
170787
|
+
// 设计: 三源聚合, 不贴来源标签 (leo: 主动推送不贴元解释文案)。
|
|
170788
|
+
// ① 桌面机会流 (默认世界流, 现状)
|
|
170789
|
+
// ② 通讯录卡: 手机号目录里查过/加过的联系人 DID → 桌面 /api/opportunities?dids= 过滤
|
|
170790
|
+
// ③ 附近卡: P2P 已连接 peers 广播 world.card 投递 (动态传送, 不等轮询)
|
|
170791
|
+
// 不可达的源如实跳过 (不假装), 但其它源照常出卡 —— 首页永远有内容。
|
|
170792
|
+
async cardFeed() {
|
|
170793
|
+
const base5 = await core.world.baseUrl();
|
|
170794
|
+
let desktop = [];
|
|
170795
|
+
if (base5) {
|
|
170796
|
+
try {
|
|
170797
|
+
const r = await fetch(`${base5}/api/opportunities?min-score=0.2&limit=20`);
|
|
170798
|
+
if (r.ok) {
|
|
170799
|
+
const d2 = await r.json();
|
|
170800
|
+
if (d2 && Array.isArray(d2.opportunities)) desktop = d2.opportunities;
|
|
170801
|
+
}
|
|
170802
|
+
} catch {
|
|
170803
|
+
}
|
|
170804
|
+
}
|
|
170805
|
+
let directory = [];
|
|
170806
|
+
const contactDids = core.directory.contactDids();
|
|
170807
|
+
if (base5 && contactDids.length) {
|
|
170808
|
+
try {
|
|
170809
|
+
const r = await fetch(`${base5}/api/opportunities?dids=${encodeURIComponent(contactDids.join(","))}&min-score=0.2&limit=20`);
|
|
170810
|
+
if (r.ok) {
|
|
170811
|
+
const d2 = await r.json();
|
|
170812
|
+
if (d2 && Array.isArray(d2.opportunities)) directory = d2.opportunities;
|
|
170813
|
+
}
|
|
170814
|
+
} catch {
|
|
170815
|
+
}
|
|
170816
|
+
}
|
|
170817
|
+
const nearby = core.world.nearbyCards();
|
|
170818
|
+
const seen = /* @__PURE__ */ new Set();
|
|
170819
|
+
const merged = [...nearby, ...directory, ...desktop].filter((c) => {
|
|
170820
|
+
if (!c || !c.id) return false;
|
|
170821
|
+
if (seen.has(String(c.id))) return false;
|
|
170822
|
+
seen.add(String(c.id));
|
|
170823
|
+
return true;
|
|
170824
|
+
});
|
|
170825
|
+
return { ok: true, count: merged.length, opportunities: merged.slice(0, 12) };
|
|
170826
|
+
},
|
|
170827
|
+
/** 附近卡暂存 (P2P world.card 入站落这里; localStorage 持久化, 重启不丢) */
|
|
170828
|
+
nearbyCards() {
|
|
170829
|
+
try {
|
|
170830
|
+
const raw = typeof localStorage !== "undefined" ? localStorage.getItem("bolloon_nearby_cards") : null;
|
|
170831
|
+
if (!raw) return [];
|
|
170832
|
+
const parsed = JSON.parse(raw);
|
|
170833
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
170834
|
+
} catch {
|
|
170835
|
+
return [];
|
|
170836
|
+
}
|
|
170837
|
+
},
|
|
170838
|
+
/** 收到附近卡 (world.card 入站 → 存 + 广播事件, 首页即时刷新, 不等 45s 轮询) */
|
|
170839
|
+
async pushNearbyCard(card, fromPeer) {
|
|
170840
|
+
if (!card || typeof card !== "object" || !card.id || !card.title) return;
|
|
170841
|
+
const entry = {
|
|
170842
|
+
...card,
|
|
170843
|
+
id: `near_${String(fromPeer || "").slice(0, 8)}_${String(card.id).slice(0, 16)}`,
|
|
170844
|
+
createdAt: card.createdAt || Date.now()
|
|
170845
|
+
};
|
|
170846
|
+
const list = core.world.nearbyCards().filter((c) => c.id !== entry.id);
|
|
170847
|
+
list.unshift(entry);
|
|
170848
|
+
const keep = list.slice(0, 30);
|
|
170849
|
+
try {
|
|
170850
|
+
if (typeof localStorage !== "undefined") localStorage.setItem("bolloon_nearby_cards", JSON.stringify(keep));
|
|
170851
|
+
} catch {
|
|
170852
|
+
}
|
|
170853
|
+
try {
|
|
170854
|
+
busBroadcast({ type: "world-card", card: entry, fromPeer });
|
|
170855
|
+
} catch {
|
|
170856
|
+
}
|
|
170857
|
+
},
|
|
170858
|
+
/** 向已连接 peers 广播请求附近卡 (动态传送: 主动问一次, 对端回 world.card) */
|
|
170859
|
+
async requestNearbyCards() {
|
|
170860
|
+
try {
|
|
170861
|
+
const p2p = await Promise.resolve().then(() => (init_mobile_p2p(), mobile_p2p_exports));
|
|
170862
|
+
const conns = p2p.getMobileP2PConnections();
|
|
170863
|
+
if (!conns.length) return { ok: false, peers: 0 };
|
|
170864
|
+
let ownDid = "";
|
|
170865
|
+
try {
|
|
170866
|
+
const st = await core.identity.status();
|
|
170867
|
+
ownDid = st?.did || "";
|
|
170868
|
+
} catch {
|
|
170869
|
+
}
|
|
170870
|
+
let sent = 0;
|
|
170871
|
+
for (const c of conns) {
|
|
170872
|
+
try {
|
|
170873
|
+
const ok = await p2p.sendMobileP2PMessage(c.peer, "world.card.request", "1", ownDid);
|
|
170874
|
+
if (ok) sent++;
|
|
170875
|
+
} catch {
|
|
170876
|
+
}
|
|
170877
|
+
}
|
|
170878
|
+
return { ok: sent > 0, peers: sent };
|
|
170879
|
+
} catch {
|
|
170880
|
+
return { ok: false, peers: 0 };
|
|
170881
|
+
}
|
|
170252
170882
|
}
|
|
170253
170883
|
},
|
|
170254
170884
|
// 手机端独立链上能力 (自己签名/发交易, 不需要电脑端; 需在设置里配 RPC)
|
|
@@ -170334,6 +170964,130 @@ ${joined}`);
|
|
|
170334
170964
|
return { ok: true, cid: await i.computeCid(value2) };
|
|
170335
170965
|
}
|
|
170336
170966
|
},
|
|
170967
|
+
// 手机号目录 (2026-10-15, 手机原生手机号功能): 手机端不持目录数据 → 转发电脑端既有端点。
|
|
170968
|
+
// 电脑端不可达 → 明确 desktop-unreachable (不假装有目录)。
|
|
170969
|
+
// 隐私: 明文手机号只随请求发给电脑端一次, 响应只回 DID/公钥/昵称 (与桌面红线一致)。
|
|
170970
|
+
directory: {
|
|
170971
|
+
/** 通讯录 DID 集合 (手机号目录查过/加过好友的联系人; localStorage 持久化) — 首页卡片传送用 */
|
|
170972
|
+
contactDids() {
|
|
170973
|
+
try {
|
|
170974
|
+
const raw = typeof localStorage !== "undefined" ? localStorage.getItem("bolloon_contact_dids") : null;
|
|
170975
|
+
if (!raw) return [];
|
|
170976
|
+
const parsed = JSON.parse(raw);
|
|
170977
|
+
return Array.isArray(parsed) ? parsed.filter((d2) => typeof d2 === "string" && d2) : [];
|
|
170978
|
+
} catch {
|
|
170979
|
+
return [];
|
|
170980
|
+
}
|
|
170981
|
+
},
|
|
170982
|
+
/** 记录一个联系 DID (查目录命中/加好友成功后调用) */
|
|
170983
|
+
rememberContactDid(did) {
|
|
170984
|
+
const d2 = String(did || "").trim();
|
|
170985
|
+
if (!d2) return;
|
|
170986
|
+
try {
|
|
170987
|
+
const list = core.directory.contactDids();
|
|
170988
|
+
if (!list.includes(d2)) {
|
|
170989
|
+
list.push(d2);
|
|
170990
|
+
const keep = list.slice(-50);
|
|
170991
|
+
if (typeof localStorage !== "undefined") localStorage.setItem("bolloon_contact_dids", JSON.stringify(keep));
|
|
170992
|
+
}
|
|
170993
|
+
} catch {
|
|
170994
|
+
}
|
|
170995
|
+
},
|
|
170996
|
+
async baseUrl() {
|
|
170997
|
+
try {
|
|
170998
|
+
const g = await Promise.resolve().then(() => (init_mobile_gateway(), mobile_gateway_exports));
|
|
170999
|
+
return String(g.getDesktopBaseUrl() || "").replace(/\/+$/, "");
|
|
171000
|
+
} catch {
|
|
171001
|
+
return "";
|
|
171002
|
+
}
|
|
171003
|
+
},
|
|
171004
|
+
async publish(phone, displayName) {
|
|
171005
|
+
const base5 = await core.directory.baseUrl();
|
|
171006
|
+
if (!base5) return { ok: false, error: "\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF (\u8BBE\u7F6E\u91CC\u586B\u684C\u9762\u5730\u5740) \u624D\u80FD\u53D1\u5E03\u624B\u673A\u53F7\u76EE\u5F55" };
|
|
171007
|
+
try {
|
|
171008
|
+
const r = await fetch(`${base5}/api/contacts/directory/publish`, {
|
|
171009
|
+
method: "POST",
|
|
171010
|
+
headers: { "Content-Type": "application/json" },
|
|
171011
|
+
body: JSON.stringify({ phone, displayName: displayName || void 0 })
|
|
171012
|
+
});
|
|
171013
|
+
const d2 = await r.json().catch(() => null);
|
|
171014
|
+
if (!r.ok) return { ok: false, error: d2 && (d2.error || d2.message) || `\u7535\u8111\u7AEF\u8FD4\u56DE ${r.status}` };
|
|
171015
|
+
return d2 || { ok: false, error: "\u7535\u8111\u7AEF\u8FD4\u56DE\u7A7A\u54CD\u5E94" };
|
|
171016
|
+
} catch (e) {
|
|
171017
|
+
return { ok: false, error: "\u7535\u8111\u7AEF\u4E0D\u53EF\u8FBE: " + String(e?.message || e).slice(0, 120) };
|
|
171018
|
+
}
|
|
171019
|
+
},
|
|
171020
|
+
async lookup(phone) {
|
|
171021
|
+
const base5 = await core.directory.baseUrl();
|
|
171022
|
+
if (!base5) return { ok: false, found: false, error: "\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF (\u8BBE\u7F6E\u91CC\u586B\u684C\u9762\u5730\u5740) \u624D\u80FD\u67E5\u624B\u673A\u53F7\u76EE\u5F55" };
|
|
171023
|
+
try {
|
|
171024
|
+
const r = await fetch(`${base5}/api/contacts/directory/lookup`, {
|
|
171025
|
+
method: "POST",
|
|
171026
|
+
headers: { "Content-Type": "application/json" },
|
|
171027
|
+
body: JSON.stringify({ phone })
|
|
171028
|
+
});
|
|
171029
|
+
const d2 = await r.json().catch(() => null);
|
|
171030
|
+
if (!r.ok) return { ok: false, found: false, error: d2 && (d2.error || d2.message) || `\u7535\u8111\u7AEF\u8FD4\u56DE ${r.status}` };
|
|
171031
|
+
if (d2 && d2.found && d2.did) core.directory.rememberContactDid(String(d2.did));
|
|
171032
|
+
return d2 || { ok: false, found: false, error: "\u7535\u8111\u7AEF\u8FD4\u56DE\u7A7A\u54CD\u5E94" };
|
|
171033
|
+
} catch (e) {
|
|
171034
|
+
return { ok: false, found: false, error: "\u7535\u8111\u7AEF\u4E0D\u53EF\u8FBE: " + String(e?.message || e).slice(0, 120) };
|
|
171035
|
+
}
|
|
171036
|
+
},
|
|
171037
|
+
async addFriend(phone, name10, message16) {
|
|
171038
|
+
const base5 = await core.directory.baseUrl();
|
|
171039
|
+
if (!base5) return { ok: false, error: "\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF (\u8BBE\u7F6E\u91CC\u586B\u684C\u9762\u5730\u5740) \u624D\u80FD\u53D1\u8D77\u597D\u53CB\u7533\u8BF7" };
|
|
171040
|
+
try {
|
|
171041
|
+
const r = await fetch(`${base5}/api/contacts/directory/add-friend`, {
|
|
171042
|
+
method: "POST",
|
|
171043
|
+
headers: { "Content-Type": "application/json" },
|
|
171044
|
+
body: JSON.stringify({ phone, name: name10 || void 0, message: message16 || void 0 })
|
|
171045
|
+
});
|
|
171046
|
+
const d2 = await r.json().catch(() => null);
|
|
171047
|
+
if (!r.ok) return { ok: false, status: r.status, error: d2 && (d2.error || d2.message) || `\u7535\u8111\u7AEF\u8FD4\u56DE ${r.status}` };
|
|
171048
|
+
if (d2 && d2.found && d2.did) core.directory.rememberContactDid(String(d2.did));
|
|
171049
|
+
return d2 || { ok: false, error: "\u7535\u8111\u7AEF\u8FD4\u56DE\u7A7A\u54CD\u5E94" };
|
|
171050
|
+
} catch (e) {
|
|
171051
|
+
return { ok: false, error: "\u7535\u8111\u7AEF\u4E0D\u53EF\u8FBE: " + String(e?.message || e).slice(0, 120) };
|
|
171052
|
+
}
|
|
171053
|
+
}
|
|
171054
|
+
},
|
|
171055
|
+
// 手机号登录 (2026-10-15): 手机号 + OTP 验证码登录 — 手机端把 bind/verify 转发电脑端,
|
|
171056
|
+
// 成功后用电脑端返回的身份绑定本机 identity (手机号即登录凭证, 不再只是昵称)。
|
|
171057
|
+
phoneAuth: {
|
|
171058
|
+
async bind(phone) {
|
|
171059
|
+
const base5 = await core.directory.baseUrl();
|
|
171060
|
+
if (!base5) return { ok: false, error: "\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF (\u8BBE\u7F6E\u91CC\u586B\u684C\u9762\u5730\u5740) \u624D\u80FD\u53D1\u9001\u9A8C\u8BC1\u7801" };
|
|
171061
|
+
try {
|
|
171062
|
+
const r = await fetch(`${base5}/api/contacts/bind`, {
|
|
171063
|
+
method: "POST",
|
|
171064
|
+
headers: { "Content-Type": "application/json" },
|
|
171065
|
+
body: JSON.stringify({ kind: "phone", value: phone, region: "CN", source: "user_input" })
|
|
171066
|
+
});
|
|
171067
|
+
const d2 = await r.json().catch(() => null);
|
|
171068
|
+
if (!r.ok) return { ok: false, error: d2 && (d2.error || d2.message) || `\u7535\u8111\u7AEF\u8FD4\u56DE ${r.status}` };
|
|
171069
|
+
return d2 || { ok: false, error: "\u7535\u8111\u7AEF\u8FD4\u56DE\u7A7A\u54CD\u5E94" };
|
|
171070
|
+
} catch (e) {
|
|
171071
|
+
return { ok: false, error: "\u7535\u8111\u7AEF\u4E0D\u53EF\u8FBE: " + String(e?.message || e).slice(0, 120) };
|
|
171072
|
+
}
|
|
171073
|
+
},
|
|
171074
|
+
async verify(contactId, challengeId, code10) {
|
|
171075
|
+
const base5 = await core.directory.baseUrl();
|
|
171076
|
+
if (!base5) return { ok: false, error: "\u9700\u8981\u7535\u8111\u7AEF\u5728\u7EBF (\u8BBE\u7F6E\u91CC\u586B\u684C\u9762\u5730\u5740) \u624D\u80FD\u9A8C\u8BC1\u9A8C\u8BC1\u7801" };
|
|
171077
|
+
try {
|
|
171078
|
+
const r = await fetch(`${base5}/api/contacts/verify`, {
|
|
171079
|
+
method: "POST",
|
|
171080
|
+
headers: { "Content-Type": "application/json" },
|
|
171081
|
+
body: JSON.stringify({ contactId, challengeId, code: code10 })
|
|
171082
|
+
});
|
|
171083
|
+
const d2 = await r.json().catch(() => null);
|
|
171084
|
+
if (!r.ok) return { ok: false, error: d2 && (d2.error || d2.message) || `\u7535\u8111\u7AEF\u8FD4\u56DE ${r.status}` };
|
|
171085
|
+
return d2 || { ok: false, error: "\u7535\u8111\u7AEF\u8FD4\u56DE\u7A7A\u54CD\u5E94" };
|
|
171086
|
+
} catch (e) {
|
|
171087
|
+
return { ok: false, error: "\u7535\u8111\u7AEF\u4E0D\u53EF\u8FBE: " + String(e?.message || e).slice(0, 120) };
|
|
171088
|
+
}
|
|
171089
|
+
}
|
|
171090
|
+
},
|
|
170337
171091
|
// 资源交易 (E2/E3/E4): 402 → 策略 → 支付 → 结果 → 信誉
|
|
170338
171092
|
trade: {
|
|
170339
171093
|
async trades(limit) {
|
|
@@ -170769,6 +171523,28 @@ ${joined}`);
|
|
|
170769
171523
|
const r = await a.runWebAgentLoop(goal, { baseUrl: llm.baseUrl, apiKey: llm.apiKey, model: llm.model, maxTokens: llm.maxTokens });
|
|
170770
171524
|
return r || "[\u5FAA\u73AF\u65E0\u8FD4\u56DE]";
|
|
170771
171525
|
},
|
|
171526
|
+
// 2026-10-16 (leo: 手机原生运行内核): 记忆/台账/指纹/多模态 查询面
|
|
171527
|
+
async memory(goal) {
|
|
171528
|
+
const a = await Promise.resolve().then(() => (init_mobile_agent(), mobile_agent_exports));
|
|
171529
|
+
const id = await a.ensureIdentity();
|
|
171530
|
+
const { MobileMemory: MobileMemory2 } = await Promise.resolve().then(() => (init_mobile_memory(), mobile_memory_exports));
|
|
171531
|
+
const m2 = new MobileMemory2(id.did);
|
|
171532
|
+
m2.restore(m2.snapshot());
|
|
171533
|
+
if (goal) {
|
|
171534
|
+
const hits = m2.recall(String(goal), { limit: 5 });
|
|
171535
|
+
return { ok: true, hits: hits.map((e) => ({ content: e.content, kind: e.kind, importance: e.importance, relation: e.relation || null })), fingerprint: m2.fingerprint() };
|
|
171536
|
+
}
|
|
171537
|
+
return { ok: true, count: m2.all().length, fingerprint: m2.fingerprint() };
|
|
171538
|
+
},
|
|
171539
|
+
async runs() {
|
|
171540
|
+
const a = await Promise.resolve().then(() => (init_mobile_agent(), mobile_agent_exports));
|
|
171541
|
+
const id = await a.ensureIdentity();
|
|
171542
|
+
return { ok: true, runs: a.listMobileRuns(id.did) };
|
|
171543
|
+
},
|
|
171544
|
+
async toolFingerprint() {
|
|
171545
|
+
const a = await Promise.resolve().then(() => (init_mobile_agent(), mobile_agent_exports));
|
|
171546
|
+
return a.toolFingerprint();
|
|
171547
|
+
},
|
|
170772
171548
|
// 2026-10-07: 持续工作 Harness (7 项能力) — 从 kernel 派生的长循环
|
|
170773
171549
|
async runHarness(goal, opts) {
|
|
170774
171550
|
const h3 = await Promise.resolve().then(() => (init_mobile_harness(), mobile_harness_exports));
|