@xopcai/xopc 0.0.336 → 0.0.338

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.
Files changed (99) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-NNn5LiRh.js → agent-avatar-dicebear-D7lwnzGf.js} +1 -1
  4. package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-DNYmmcXR.js → agent-defaults-settings-panel-Bsf_C5G-.js} +1 -1
  5. package/dist/gateway/static/root/assets/{agent-plugin-dialog-DfAd5ptC.js → agent-plugin-dialog-D1bd54F4.js} +1 -1
  6. package/dist/gateway/static/root/assets/{agents-DEgksLoh.js → agents-D9LauN1U.js} +3 -3
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-BR-Ks3tk.js → app-management-settings-panel-Blp7cgN9.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-DQn5060f.js → appearance-settings-CKEHZAl0.js} +1 -1
  9. package/dist/gateway/static/root/assets/{apps-page-BghyAP7f.js → apps-page-CHCvTpXO.js} +1 -1
  10. package/dist/gateway/static/root/assets/{archive-plugin-DSyujAeW.js → archive-plugin-BTQaxZoS.js} +1 -1
  11. package/dist/gateway/static/root/assets/{automations-page-dl2URNWU.js → automations-page-DT_SzVIu.js} +1 -1
  12. package/dist/gateway/static/root/assets/{automations-workspace-C_3pzyHa.js → automations-workspace-xyYt1P_F.js} +1 -1
  13. package/dist/gateway/static/root/assets/{binary-plugins-Bi6hXKEs.js → binary-plugins-CeeqvrXF.js} +1 -1
  14. package/dist/gateway/static/root/assets/{block-editor-aqBwqSqa.js → block-editor-B5Si55pP.js} +1 -1
  15. package/dist/gateway/static/root/assets/{browser-automations-page-BxLzYDlU.js → browser-automations-page-DApYWSvZ.js} +1 -1
  16. package/dist/gateway/static/root/assets/{browser-settings-page-wiVToOs2.js → browser-settings-page-B4FRZt79.js} +1 -1
  17. package/dist/gateway/static/root/assets/{capabilities-page-9SxFc1Gk.js → capabilities-page-Dm3rO9D9.js} +2 -2
  18. package/dist/gateway/static/root/assets/{capabilities-settings-panel-6o3ePBVe.js → capabilities-settings-panel-BxWC1fto.js} +1 -1
  19. package/dist/gateway/static/root/assets/{capability-header-actions-o7tnD40O.js → capability-header-actions-ulVgiGk5.js} +1 -1
  20. package/dist/gateway/static/root/assets/{channels-BiDkmHQX.js → channels-CxjiPi_K.js} +1 -1
  21. package/dist/gateway/static/root/assets/{chat-terminal-dock-DgaRuyCk.js → chat-terminal-dock-k7s5v_aC.js} +1 -1
  22. package/dist/gateway/static/root/assets/{computer-settings-page-BScTGz9r.js → computer-settings-page-B6aKe8Pz.js} +1 -1
  23. package/dist/gateway/static/root/assets/{connector-service-page-D19h4o3H.js → connector-service-page-DS_dCuYu.js} +1 -1
  24. package/dist/gateway/static/root/assets/{connectors-page-nb20ZtXB.js → connectors-page-CbLxkIGU.js} +1 -1
  25. package/dist/gateway/static/root/assets/{date-picker-4XKcj75n.js → date-picker-DOl4D6Og.js} +1 -1
  26. package/dist/gateway/static/root/assets/{dependency-picker-BLLIIM2j.js → dependency-picker-DTZpqUW9.js} +1 -1
  27. package/dist/gateway/static/root/assets/{desktop-pet-F-odgilG.js → desktop-pet-A9AmyZFd.js} +1 -1
  28. package/dist/gateway/static/root/assets/{desktop-pet-settings-C1bi6P68.js → desktop-pet-settings-D4NMQfeC.js} +1 -1
  29. package/dist/gateway/static/root/assets/{device-pairing-wizard-C8brKN1N.js → device-pairing-wizard-B19IahuA.js} +1 -1
  30. package/dist/gateway/static/root/assets/{directory-picker-path-field-BQROUAVR.js → directory-picker-path-field-D_92XGjD.js} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-debug-page-j6UKcafs.js → extension-debug-page-CJEVTdVQ.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-page-B-MC_llQ.js → extension-page-Bo_QfERv.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-settings-page-E7iPqGV_.js → extension-settings-page-DyQKVwEg.js} +1 -1
  34. package/dist/gateway/static/root/assets/{gateway-settings-CxhocvFl.js → gateway-settings-Cm57iUdQ.js} +1 -1
  35. package/dist/gateway/static/root/assets/{home-page-DKJTrzRz.js → home-page-NBbSjN9G.js} +1 -1
  36. package/dist/gateway/static/root/assets/{imports-page-BH0bK4Fi.js → imports-page-B9T8hM43.js} +1 -1
  37. package/dist/gateway/static/root/assets/{index-Z_w672TD.js → index-B-NICmRj.js} +9 -9
  38. package/dist/gateway/static/root/assets/index-Bd7mDZFV.css +1 -0
  39. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-DqYlPK6e.js → keyboard-shortcuts-settings-DU3r7EWf.js} +1 -1
  40. package/dist/gateway/static/root/assets/{local-app-workbench-page-DJFEtiQ-.js → local-app-workbench-page-ChpwIJTp.js} +1 -1
  41. package/dist/gateway/static/root/assets/{local-apps-page-soLqlAnz.js → local-apps-page-Cz8SE9jo.js} +1 -1
  42. package/dist/gateway/static/root/assets/{logs-page-CG21jjyw.js → logs-page-1oTbkOs_.js} +1 -1
  43. package/dist/gateway/static/root/assets/{management-settings-lZjA5aEo.js → management-settings-BXeXgpGe.js} +1 -1
  44. package/dist/gateway/static/root/assets/{media-plugins-Bx67LlgO.js → media-plugins-DU8A_g9F.js} +1 -1
  45. package/dist/gateway/static/root/assets/{note-detail-panel-C-uwlkj6.js → note-detail-panel-D4KpPyvg.js} +3 -3
  46. package/dist/gateway/static/root/assets/{notes-page-BvJNHWMx.js → notes-page-DvUYRsn1.js} +1 -1
  47. package/dist/gateway/static/root/assets/{page-context-capture-button-X1LphhT2.js → page-context-capture-button-BmpkysKb.js} +1 -1
  48. package/dist/gateway/static/root/assets/{page-tabs-Bvuo9Cp9.js → page-tabs-BYVaSh2t.js} +1 -1
  49. package/dist/gateway/static/root/assets/{preview-open-alternatives-CnosDbXg.js → preview-open-alternatives-C3EhH0bN.js} +1 -1
  50. package/dist/gateway/static/root/assets/{product-open-page-DH38UFyT.js → product-open-page-CR5vTalE.js} +1 -1
  51. package/dist/gateway/static/root/assets/{project-detail-page-DzS67sf6.js → project-detail-page-5e5nW6aK.js} +1 -1
  52. package/dist/gateway/static/root/assets/{projects-page-D8tBTON4.js → projects-page-BEcT_inq.js} +1 -1
  53. package/dist/gateway/static/root/assets/{remote-access-hub-Bwsg9beH.js → remote-access-hub-B1qvhjS4.js} +1 -1
  54. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BcTGjpqr.js → runtime-tools-settings-panel-B_0CfBAT.js} +1 -1
  55. package/dist/gateway/static/root/assets/{scenes-page-jbTHblQV.js → scenes-page-DUL9nosj.js} +1 -1
  56. package/dist/gateway/static/root/assets/{schema-form-BtuMUmjZ.js → schema-form-va7QWGEA.js} +1 -1
  57. package/dist/gateway/static/root/assets/{sessions-page-Dm2qIlJZ.js → sessions-page-vVCF7_fY.js} +1 -1
  58. package/dist/gateway/static/root/assets/settings-advanced-gate-BArcldzX.js +1 -0
  59. package/dist/gateway/static/root/assets/{settings-form-section-6QQu_bi3.js → settings-form-section-CFLiI9wl.js} +1 -1
  60. package/dist/gateway/static/root/assets/{settings-page-B000AkmV.js → settings-page-uNvPtLCL.js} +1 -1
  61. package/dist/gateway/static/root/assets/{setup-status-panel-Ogru9NdY.js → setup-status-panel-_w4HzIN6.js} +1 -1
  62. package/dist/gateway/static/root/assets/{share-preview-page-BMO2ZoqU.js → share-preview-page-ByyeLleG.js} +1 -1
  63. package/dist/gateway/static/root/assets/{shares-settings-CtmBtL1f.js → shares-settings-BiLA03S-.js} +1 -1
  64. package/dist/gateway/static/root/assets/{skill-api-DzFmRoGt.js → skill-api-DXOq4iWa.js} +1 -1
  65. package/dist/gateway/static/root/assets/{skill-reload-api-DcyuV6CY.js → skill-reload-api-D2uzVAZq.js} +1 -1
  66. package/dist/gateway/static/root/assets/{skills-page-DQBfVKZv.js → skills-page-BSnyNWx-.js} +1 -1
  67. package/dist/gateway/static/root/assets/{system-settings-panel-CYr7jyqK.js → system-settings-panel-BPoF82eS.js} +1 -1
  68. package/dist/gateway/static/root/assets/{task-detail-page-CwxfwNqK.js → task-detail-page-DvVPVptc.js} +1 -1
  69. package/dist/gateway/static/root/assets/{tunnel-api-COxKjwYF.js → tunnel-api-CA92GwfO.js} +1 -1
  70. package/dist/gateway/static/root/assets/{usage-page-CJDBoCpM.js → usage-page-B7F4u0TA.js} +1 -1
  71. package/dist/gateway/static/root/assets/{use-autosave-Tiykntd0.js → use-autosave-Ca0tBkcs.js} +1 -1
  72. package/dist/gateway/static/root/assets/{user-model-page-BhGePvWd.js → user-model-page-aeTxECGB.js} +1 -1
  73. package/dist/gateway/static/root/assets/{voice-api-key-field-Bny9WMTa.js → voice-api-key-field-BL6CNWHA.js} +1 -1
  74. package/dist/gateway/static/root/assets/{work-discovery-overlay-B6FMtX3w.js → work-discovery-overlay-CSc9Xv9J.js} +1 -1
  75. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-DoOMpqPg.js → workflow-run-setup-panel-hho2_zza.js} +1 -1
  76. package/dist/gateway/static/root/assets/{workflows-page-b2v3lMJ1.js → workflows-page-N--cEC4e.js} +1 -1
  77. package/dist/gateway/static/root/index.html +2 -2
  78. package/dist/package.js +1 -1
  79. package/dist/packages/gateway-contract/src/index.js +2 -0
  80. package/dist/packages/gateway-contract/src/mobile-user-understanding.js +140 -0
  81. package/dist/src/agent/source-context/index.d.ts +1 -0
  82. package/dist/src/agent/source-context/index.js +2 -1
  83. package/dist/src/agent/source-context/types.d.ts +2 -2
  84. package/dist/src/agent/source-context/types.js +1 -1
  85. package/dist/src/agent/source-context/user-assertion-context.d.ts +2 -0
  86. package/dist/src/agent/source-context/user-assertion-context.js +30 -0
  87. package/dist/src/cli/index.js +1 -1
  88. package/dist/src/gateway/hono/routes/session-input-handler.d.ts +2 -2
  89. package/dist/src/gateway/hono/routes/sessions.js +7 -0
  90. package/dist/src/gateway/hono/routes/user-model.js +202 -0
  91. package/dist/src/gateway/run-loop.js +1 -1
  92. package/dist/src/gateway/security/gateway-scopes.js +3 -0
  93. package/dist/src/gateway/service/session-input-coordinator.js +1 -1
  94. package/dist/src/gateway/service.js +6 -1
  95. package/dist/src/user-model/goals.d.ts +1 -1
  96. package/dist/src/user-model/goals.js +2 -1
  97. package/package.json +3 -3
  98. package/dist/gateway/static/root/assets/index-DbLEkdNg.css +0 -1
  99. package/dist/gateway/static/root/assets/settings-advanced-gate-9sku1sUP.js +0 -1
@@ -0,0 +1,30 @@
1
+ import { getAssertionSlot, getUserAssertion, listUserAssertionSources } from "../../user-model/repository.js";
2
+ import { canUseAssertion } from "../../user-model/usage-policy.js";
3
+ import "../../user-model/index.js";
4
+ //#region src/agent/source-context/user-assertion-context.ts
5
+ function buildUserAssertionAgentContext(assertionId, expectedVersion, agentId) {
6
+ const assertion = getUserAssertion(assertionId);
7
+ const slot = assertion ? getAssertionSlot(assertion.slotId) : void 0;
8
+ if (!assertion || !slot || slot.scope.type !== "global" || expectedVersion && expectedVersion !== String(assertion.recordedAt) || !canUseAssertion(assertion, Date.now(), {
9
+ use: "answer",
10
+ agentId
11
+ })) return null;
12
+ const sources = listUserAssertionSources([assertion.id]).get(assertion.id) ?? [];
13
+ return {
14
+ kind: "user_assertion",
15
+ sourceId: assertion.id,
16
+ version: String(assertion.recordedAt),
17
+ title: assertion.statement,
18
+ text: JSON.stringify({
19
+ id: assertion.id,
20
+ statement: assertion.statement,
21
+ predicate: slot.predicate,
22
+ scope: slot.scope,
23
+ status: assertion.status,
24
+ authority: assertion.authority,
25
+ sources
26
+ }, null, 2)
27
+ };
28
+ }
29
+ //#endregion
30
+ export { buildUserAssertionAgentContext };
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { version } from "../../package.js";
3
- import { bootstrapApplicationStateSync } from "../bootstrap/application-state.js";
4
3
  import { isHelpOrVersionInvocation } from "./argv.js";
5
4
  import { ROOT_COMMAND_DESCRIPTION } from "./command-manifest.js";
6
5
  import { loadAllCommands, resolveCommandName, tryLoadCommand } from "./command-loaders.js";
7
6
  import { getContextWithOpts, parsedOpts } from "./context.js";
7
+ import { bootstrapApplicationStateSync } from "../bootstrap/application-state.js";
8
8
  import { Command } from "commander";
9
9
  //#region src/cli/index.ts
10
10
  /** Command wiring and `program.parse`; executable entry is `bin.ts` (log preset before logger init). */
@@ -73,7 +73,7 @@ export declare function submitSessionInput(c: Context, deps: AuthenticatedRouteD
73
73
  }[];
74
74
  contextSnapshots?: {
75
75
  refId?: string;
76
- kind: import("../../../agent/source-context/types.js").SessionSourceBinding["kind"] | "task" | "file" | "session" | "mcp_resource" | "browser_tab" | "browser_page" | "app_context";
76
+ kind: import("../../../agent/source-context/types.js").SessionSourceBinding["kind"] | "task" | "file" | "session" | "mcp_resource" | "browser_tab" | "browser_page" | "app_context" | "user_assertion";
77
77
  sourceId: string;
78
78
  version: string;
79
79
  title: string;
@@ -217,7 +217,7 @@ export declare function replaceLatestSessionTurn(c: Context, deps: Authenticated
217
217
  }[];
218
218
  contextSnapshots?: {
219
219
  refId?: string;
220
- kind: import("../../../agent/source-context/types.js").SessionSourceBinding["kind"] | "task" | "file" | "session" | "mcp_resource" | "browser_tab" | "browser_page" | "app_context";
220
+ kind: import("../../../agent/source-context/types.js").SessionSourceBinding["kind"] | "task" | "file" | "session" | "mcp_resource" | "browser_tab" | "browser_page" | "app_context" | "user_assertion";
221
221
  sourceId: string;
222
222
  version: string;
223
223
  title: string;
@@ -358,6 +358,13 @@ function registerSessionsRoutes(authenticated, deps) {
358
358
  });
359
359
  authenticated.get("/api/sessions/:key/agent-config", async (c) => {
360
360
  const key = c.req.param("key");
361
+ if (!await service.sessions.getSession(key)) return c.json({
362
+ ok: false,
363
+ error: {
364
+ code: "NOT_FOUND",
365
+ message: "Session not found"
366
+ }
367
+ }, 404);
361
368
  const payload = await service.sessions.getFixedAgentConfig(key);
362
369
  return c.json({
363
370
  ok: true,
@@ -116,6 +116,19 @@ const RULE_STATUSES = new Set([
116
116
  "disabled",
117
117
  "archived"
118
118
  ]);
119
+ const MOBILE_PROFILE_PREDICATES = new Set([
120
+ "identity.call_name",
121
+ "identity.role",
122
+ "identity.pronouns",
123
+ "preference.timezone",
124
+ "preference.locale"
125
+ ]);
126
+ const MOBILE_ASSERTION_FILTERS = new Set([
127
+ "all",
128
+ "explicit",
129
+ "learned",
130
+ "review"
131
+ ]);
119
132
  function assertionView(assertion, sources = listUserAssertionSources([assertion.id]).get(assertion.id) ?? []) {
120
133
  const slot = getAssertionSlot(assertion.slotId);
121
134
  if (!slot) throw new Error(`Assertion slot not found: ${assertion.slotId}`);
@@ -131,6 +144,70 @@ function assertionView(assertion, sources = listUserAssertionSources([assertion.
131
144
  })
132
145
  };
133
146
  }
147
+ function mobileAssertionView(assertion, sources = listUserAssertionSources([assertion.id]).get(assertion.id) ?? []) {
148
+ const view = assertionView(assertion, sources);
149
+ return {
150
+ id: view.id,
151
+ predicate: view.predicate,
152
+ statement: view.statement,
153
+ kind: view.kind,
154
+ status: view.status,
155
+ authority: view.authority,
156
+ usable: view.usable,
157
+ confidence: view.confidence,
158
+ volatility: view.volatility,
159
+ layer: view.layer,
160
+ independentSourceCount: view.independentSourceCount,
161
+ observedAt: view.observedAt,
162
+ recordedAt: view.recordedAt,
163
+ ...view.validTo === void 0 ? {} : { validTo: view.validTo },
164
+ scope: view.scope,
165
+ sources: view.sources
166
+ };
167
+ }
168
+ function isMobileAssertion(item, filter) {
169
+ if (item.scope.type !== "global" || MOBILE_PROFILE_PREDICATES.has(item.predicate)) return false;
170
+ if (filter === "review") return item.status === "needs_review" || item.status === "conflicted";
171
+ if (!item.usable) return false;
172
+ if (filter === "explicit") return item.authority === "user_explicit";
173
+ if (filter === "learned") return item.authority !== "user_explicit";
174
+ return true;
175
+ }
176
+ function normalizeMobileSearch(value, maxLength) {
177
+ const normalized = (value ?? "").normalize("NFKC").trim().toLocaleLowerCase();
178
+ return maxLength === void 0 ? normalized : normalized.slice(0, maxLength);
179
+ }
180
+ function matchesMobileSearch(item, query) {
181
+ if (!query) return true;
182
+ const statusAliases = item.status === "needs_review" || item.status === "conflicted" ? "needs review review 待确认 需要确认" : item.status;
183
+ const authorityAliases = item.authority === "user_explicit" ? "explicit user said 你明确说过 用户明确" : "learned inferred 逐渐学到 系统推断";
184
+ return normalizeMobileSearch([
185
+ item.statement,
186
+ item.predicate,
187
+ item.kind,
188
+ statusAliases,
189
+ authorityAliases,
190
+ item.scope.type,
191
+ item.scope.id ?? "",
192
+ ...item.sources.flatMap((source) => [
193
+ source.label ?? "",
194
+ source.kind,
195
+ source.category ?? ""
196
+ ])
197
+ ].join(" ")).includes(query);
198
+ }
199
+ function encodeMobileCursor(item) {
200
+ return Buffer.from(JSON.stringify([item.recordedAt, item.id])).toString("base64url");
201
+ }
202
+ function decodeMobileCursor(value) {
203
+ if (!value) return void 0;
204
+ try {
205
+ const parsed = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
206
+ return Array.isArray(parsed) && typeof parsed[0] === "number" && typeof parsed[1] === "string" ? [parsed[0], parsed[1]] : void 0;
207
+ } catch {
208
+ return;
209
+ }
210
+ }
134
211
  function registerUserModelRoutes(authenticated, deps) {
135
212
  const write = deps.strictRateLimitMiddleware;
136
213
  authenticated.get("/api/user-model", (c) => {
@@ -184,6 +261,78 @@ function registerUserModelRoutes(authenticated, deps) {
184
261
  }
185
262
  });
186
263
  });
264
+ authenticated.get("/api/user-model/mobile-summary", (c) => {
265
+ const rawAssertions = listUserAssertions({
266
+ statuses: [
267
+ "active",
268
+ "candidate",
269
+ "needs_review",
270
+ "conflicted",
271
+ "stale"
272
+ ],
273
+ limit: 2e3
274
+ });
275
+ const sourceMap = listUserAssertionSources(rawAssertions.map((item) => item.id));
276
+ const items = rawAssertions.map((item) => mobileAssertionView(item, sourceMap.get(item.id) ?? []));
277
+ const visible = items.filter((item) => isMobileAssertion(item, "all"));
278
+ const review = items.filter((item) => isMobileAssertion(item, "review"));
279
+ const profile = getUserProfileSnapshot();
280
+ const goals = listUserGoals();
281
+ const now = Date.now();
282
+ const primary = listPriorityWindows().filter((item) => item.status === "active" && item.rank === "primary" && item.validTo >= now).sort((left, right) => (right.declaredImportance ?? right.urgency) - (left.declaredImportance ?? left.urgency))[0];
283
+ const primaryGoal = primary?.targetType === "goal" ? goals.find((goal) => goal.id === primary.targetId) : void 0;
284
+ const primaryTitle = primaryGoal?.title ?? (primary && !/^[0-9a-f]{8}-[0-9a-f-]{20,}$/i.test(primary.targetId) ? primary.targetId : void 0);
285
+ const mobileGoals = goals.filter((goal) => goal.status === "active" || goal.status === "paused" || goal.status === "proposed").sort((left, right) => {
286
+ if (left.id === primaryGoal?.id) return -1;
287
+ if (right.id === primaryGoal?.id) return 1;
288
+ if (left.status === "active" && right.status !== "active") return -1;
289
+ if (right.status === "active" && left.status !== "active") return 1;
290
+ return (left.targetAt ?? Number.MAX_SAFE_INTEGER) - (right.targetAt ?? Number.MAX_SAFE_INTEGER) || right.updatedAt - left.updatedAt;
291
+ }).slice(0, 5).map((goal) => ({
292
+ id: goal.id,
293
+ title: goal.title,
294
+ desiredOutcome: goal.desiredOutcome,
295
+ status: goal.status,
296
+ ...goal.targetAt === void 0 ? {} : { targetAt: goal.targetAt },
297
+ updatedAt: goal.updatedAt,
298
+ isPrimary: goal.id === primaryGoal?.id
299
+ }));
300
+ const result = {
301
+ profile: {
302
+ callName: profile.callName ?? "",
303
+ role: profile.role ?? "",
304
+ pronouns: profile.pronouns ?? "",
305
+ timezone: profile.timezone ?? "",
306
+ locale: profile.locale ?? ""
307
+ },
308
+ suggestedCallName: profile.callName || machineCallName(),
309
+ counts: {
310
+ total: visible.length,
311
+ explicit: visible.filter((item) => item.authority === "user_explicit").length,
312
+ learned: visible.filter((item) => item.authority !== "user_explicit").length,
313
+ review: review.length,
314
+ workMemory: listKnowledgeItems({
315
+ statuses: ["active"],
316
+ recordClass: "memory",
317
+ limit: 2e3
318
+ }).length
319
+ },
320
+ ...primary && primaryTitle ? { primaryFocus: {
321
+ id: primary.id,
322
+ title: primaryTitle,
323
+ ...primaryGoal?.desiredOutcome ? { desiredOutcome: primaryGoal.desiredOutcome } : {},
324
+ validTo: primary.validTo
325
+ } } : {},
326
+ goals: mobileGoals,
327
+ recent: [...visible].sort((left, right) => right.recordedAt - left.recordedAt).slice(0, 3),
328
+ rules: listCollaborationRules().filter((rule) => rule.status === "active").slice(0, 3).map((rule) => ({
329
+ id: rule.id,
330
+ statement: rule.statement,
331
+ category: rule.category
332
+ }))
333
+ };
334
+ return c.json(result);
335
+ });
187
336
  authenticated.patch("/api/user-model/profile", write, async (c) => {
188
337
  const input = await body(c);
189
338
  if (!input) return c.json({ error: "A profile patch is required" }, 400);
@@ -224,6 +373,34 @@ function registerUserModelRoutes(authenticated, deps) {
224
373
  }
225
374
  });
226
375
  authenticated.get("/api/user-model/assertions", (c) => {
376
+ if (c.req.query("view") === "mobile") {
377
+ const filter = c.req.query("filter") ?? "all";
378
+ if (!MOBILE_ASSERTION_FILTERS.has(filter)) return c.json({ error: "Invalid mobile assertion filter" }, 400);
379
+ const requestedLimit = Number(c.req.query("limit") ?? 20);
380
+ const limit = Number.isFinite(requestedLimit) ? Math.max(1, Math.min(50, Math.trunc(requestedLimit))) : 20;
381
+ const query = normalizeMobileSearch(c.req.query("q"), 100);
382
+ const cursorValue = c.req.query("cursor");
383
+ const cursor = decodeMobileCursor(cursorValue);
384
+ if (cursorValue && !cursor) return c.json({ error: "Invalid cursor" }, 400);
385
+ const assertions = listUserAssertions({
386
+ statuses: [
387
+ "active",
388
+ "candidate",
389
+ "needs_review",
390
+ "conflicted",
391
+ "stale"
392
+ ],
393
+ limit: 2e3
394
+ });
395
+ const sourceMap = listUserAssertionSources(assertions.map((item) => item.id));
396
+ const filtered = assertions.map((item) => mobileAssertionView(item, sourceMap.get(item.id) ?? [])).filter((item) => isMobileAssertion(item, filter)).filter((item) => matchesMobileSearch(item, query)).sort((left, right) => right.recordedAt - left.recordedAt || right.id.localeCompare(left.id)).filter((item) => !cursor || item.recordedAt < cursor[0] || item.recordedAt === cursor[0] && item.id.localeCompare(cursor[1]) < 0);
397
+ const items = filtered.slice(0, limit);
398
+ const result = {
399
+ items,
400
+ ...filtered.length > limit && items.length ? { nextCursor: encodeMobileCursor(items[items.length - 1]) } : {}
401
+ };
402
+ return c.json(result);
403
+ }
227
404
  const requested = c.req.query("status")?.split(",").filter(Boolean);
228
405
  if (requested?.some((status) => !ASSERTION_STATUSES.has(status))) return c.json({ error: "Invalid assertion status" }, 400);
229
406
  const assertions = listUserAssertions({
@@ -363,6 +540,31 @@ function registerUserModelRoutes(authenticated, deps) {
363
540
  const goal = setUserGoalStatus(c.req.param("id"), status);
364
541
  return goal ? c.json({ goal }) : c.json({ error: "Goal not found" }, 404);
365
542
  });
543
+ authenticated.patch("/api/user-model/goals/:id", write, async (c) => {
544
+ const input = await body(c);
545
+ const current = listUserGoals().find((goal) => goal.id === c.req.param("id"));
546
+ if (!current) return c.json({ error: "Goal not found" }, 404);
547
+ if (!input) return c.json({ error: "A goal patch is required" }, 400);
548
+ const title = Object.hasOwn(input, "title") ? input.title : current.title;
549
+ const desiredOutcome = Object.hasOwn(input, "desiredOutcome") ? input.desiredOutcome : current.desiredOutcome;
550
+ const rawTargetAt = Object.hasOwn(input, "targetAt") ? input.targetAt : current.targetAt;
551
+ const status = Object.hasOwn(input, "status") ? input.status : current.status;
552
+ if (typeof title !== "string" || typeof desiredOutcome !== "string" || rawTargetAt !== void 0 && rawTargetAt !== null && typeof rawTargetAt !== "number" || typeof status !== "string" || !GOAL_STATUSES.has(status)) return c.json({ error: "Goal patch fields have invalid types" }, 400);
553
+ const targetAt = typeof rawTargetAt === "number" ? rawTargetAt : rawTargetAt === null ? null : void 0;
554
+ try {
555
+ const goal = runSqliteWriteTransaction(() => {
556
+ const updated = updateUserGoal(current.id, {
557
+ title,
558
+ desiredOutcome,
559
+ ...targetAt === void 0 ? {} : { targetAt }
560
+ });
561
+ return status === current.status ? updated : setUserGoalStatus(current.id, status);
562
+ });
563
+ return c.json({ goal });
564
+ } catch (error) {
565
+ return c.json({ error: errorMessage(error) }, 400);
566
+ }
567
+ });
366
568
  authenticated.get("/api/user-model/rules", (c) => c.json({ rules: listCollaborationRules() }));
367
569
  authenticated.post("/api/user-model/rules", write, async (c) => {
368
570
  const input = await body(c);
@@ -1,7 +1,7 @@
1
1
  import { acquireGatewayLock } from "./lock.js";
2
2
  import { loadConfig } from "../config/loader.js";
3
- import { runBootstrapMigrationsSync } from "../migrations/runner.js";
4
3
  import "../config/index.js";
4
+ import { runBootstrapMigrationsSync } from "../migrations/runner.js";
5
5
  import { isRestartEnabled } from "../config/commands.flags.js";
6
6
  import { restartGatewayProcessWithFreshPid } from "./respawn.js";
7
7
  import { consumeGatewayRestartIntentSync, consumeGatewaySigusr1RestartAuthorization, isGatewaySigusr1RestartExternallyAllowed, resetGatewayRestartStateForInProcessRestart, scheduleGatewaySigusr1Restart, setGatewaySigusr1RestartPolicy } from "../infra/restart.js";
@@ -35,6 +35,9 @@ function requiredGatewayScope(method, path) {
35
35
  if (path === "/api/voice/selection" || path === "/api/voice/catalog/refresh") return "voice.configure";
36
36
  if (path.startsWith("/api/voice") || path.startsWith("/api/media") || path.startsWith("/api/clarifications")) return "sessions.write";
37
37
  if (path.startsWith("/api/commands") || path.startsWith("/api/skills")) return methodScope(method, "agents.read", "gateway.admin");
38
+ if (path === "/api/user-model/refresh" || path.startsWith("/api/user-model/refresh/")) return "gateway.admin";
39
+ if (path === "/api/user-model" || path.startsWith("/api/user-model/")) return methodScope(method, "workspace.read", "workspace.write");
40
+ if (path === "/api/knowledge-memory" || path.startsWith("/api/knowledge-memory/")) return methodScope(method, "workspace.read", "workspace.write");
38
41
  if (path === "/api/discussions" || path.startsWith("/api/discussions/") || path === "/api/discussion-capture/settings" || path.startsWith("/api/workspace") || path.startsWith("/api/files") || path.startsWith("/api/projects") || path.startsWith("/api/notes") || path.startsWith("/api/shares")) return methodScope(method, "workspace.read", "workspace.write");
39
42
  return "gateway.admin";
40
43
  }
@@ -26,7 +26,7 @@ function sameAppContext(input, existing) {
26
26
  return isDeepStrictEqual(identity(input.sourceContexts), identity(existing.contextSnapshots));
27
27
  }
28
28
  function contextRefsMatchFrozenSnapshot(requested, frozen) {
29
- const editable = frozen?.filter((ref) => ref.kind === "note" || ref.kind === "task" || ref.kind === "file" || ref.kind === "session" || ref.kind === "browser_tab" || ref.kind === "mcp_resource") ?? [];
29
+ const editable = frozen?.filter((ref) => ref.kind === "note" || ref.kind === "task" || ref.kind === "file" || ref.kind === "session" || ref.kind === "browser_tab" || ref.kind === "mcp_resource" || ref.kind === "user_assertion") ?? [];
30
30
  if (requested.length !== editable.length) return false;
31
31
  return requested.every((ref, index) => {
32
32
  const snapshot = editable[index];
@@ -65,7 +65,6 @@ import { MessageBus, MessageBusShutdownError } from "../infra/bus/queue.js";
65
65
  import "../infra/bus/index.js";
66
66
  import { collectMediaUrisFromMessages, deleteMediaUris } from "../media/session-references.js";
67
67
  import { AgentService } from "../agent/service.js";
68
- import { bootstrapApplicationStateSync } from "../bootstrap/application-state.js";
69
68
  import { setPairingBroadcastSink } from "../channels/pairing/pairing-events.js";
70
69
  import { registerClarificationChannelRuntime } from "./clarify-runtime.js";
71
70
  import { ChannelManager } from "../channels/manager.js";
@@ -73,6 +72,7 @@ import { ChatPreviewService } from "../chat-previews/service.js";
73
72
  import "../chat-previews/index.js";
74
73
  import { getWorkspacePath } from "../config/workspace-path-helpers.js";
75
74
  import "../config/index.js";
75
+ import { bootstrapApplicationStateSync } from "../bootstrap/application-state.js";
76
76
  import { ManagedComposioEventPoller } from "../connectors/composio-managed-events.js";
77
77
  import { startConnectedKnowledgeCoordinator } from "../knowledge/coordinator.js";
78
78
  import "../knowledge/index.js";
@@ -98,6 +98,7 @@ import { buildFileAgentContext } from "../agent/source-context/file-context.js";
98
98
  import { buildSessionAgentContext } from "../agent/source-context/session-context.js";
99
99
  import { buildMcpResourceAgentContext } from "../agent/source-context/mcp-resource-context.js";
100
100
  import { buildBrowserTabAgentContext } from "../agent/source-context/browser-tab.js";
101
+ import { buildUserAssertionAgentContext } from "../agent/source-context/user-assertion-context.js";
101
102
  import { WorkDiscoveryService } from "../work-discovery/service.js";
102
103
  import { buildVoiceMemoryContext } from "../voice/realtime/memory-context.js";
103
104
  import { buildVoicePersonaContext } from "../voice/realtime/persona-context.js";
@@ -504,6 +505,10 @@ var GatewayService = class {
504
505
  getConfig: () => this.config,
505
506
  resolveTurnContext: async (ref, conversationId) => {
506
507
  if (ref.kind === "task") return buildTaskAgentContext(new TaskRepository().get(ref.sourceId), ref.expectedVersion);
508
+ if (ref.kind === "user_assertion") {
509
+ const metadata = await this.sessionIndex.getSessionMetadata(conversationId);
510
+ return buildUserAssertionAgentContext(ref.sourceId, ref.expectedVersion, metadata?.agentId);
511
+ }
507
512
  if (ref.kind === "file") {
508
513
  const files = getGatewayFileSpaceService(this);
509
514
  const space = await files.forContext("session", conversationId).catch(() => null);
@@ -50,7 +50,7 @@ export declare function setUserGoalStatus(id: string, status: UserGoalStatus, no
50
50
  export declare function updateUserGoal(id: string, input: {
51
51
  title: string;
52
52
  desiredOutcome: string;
53
- targetAt?: number;
53
+ targetAt?: number | null;
54
54
  }, now?: number): UserGoal | undefined;
55
55
  export declare function listPriorityWindows(principalId?: string): UserPriorityWindow[];
56
56
  export declare function createPriorityWindow(input: {
@@ -65,8 +65,9 @@ function updateUserGoal(id, input, now = Date.now()) {
65
65
  db.prepare(`INSERT INTO user_goal_revisions (
66
66
  revision_id, goal_id, title, desired_outcome, created_by, change_reason, created_at
67
67
  ) VALUES (?, ?, ?, ?, 'user', 'Goal edited by user.', ?)`).run(revisionId, id, title, desiredOutcome, now);
68
+ const targetAt = input.targetAt === void 0 ? current.targetAt ?? null : input.targetAt;
68
69
  db.prepare(`UPDATE user_goals SET current_revision_id = ?, target_at = ?, updated_at = ?
69
- WHERE goal_id = ?`).run(revisionId, input.targetAt ?? current.targetAt ?? null, now, id);
70
+ WHERE goal_id = ?`).run(revisionId, targetAt, now, id);
70
71
  });
71
72
  return listUserGoals().find((goal) => goal.id === id);
72
73
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xopcai/xopc",
3
- "version": "0.0.336",
3
+ "version": "0.0.338",
4
4
  "description": "Local-first personal AI for the one-person company, with durable context, tasks, workflows, automations, and local or cloud models.",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -159,11 +159,11 @@
159
159
  "vitest": "4.1.5",
160
160
  "vue": "^3.5.35",
161
161
  "@xopcai/browser-control-contract": "0.1.0",
162
- "@xopcai/endpoint-tools-client": "0.0.0",
163
162
  "@xopcai/computer-control-contract": "0.1.0",
163
+ "@xopcai/endpoint-tools-client": "0.0.0",
164
164
  "@xopcai/endpoint-tools-protocol": "0.0.0",
165
- "@xopcai/realtime-client": "0.0.0",
166
165
  "@xopcai/gateway-contract": "0.0.0",
166
+ "@xopcai/realtime-client": "0.0.0",
167
167
  "@xopcai/realtime-protocol": "0.0.0"
168
168
  },
169
169
  "scripts": {