openfox 2.0.0-beta.10 → 2.0.0-beta.13

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 (34) hide show
  1. package/dist/{chat-handler-6COBLIDR.js → chat-handler-DKX2XFHA.js} +9 -9
  2. package/dist/{chunk-LR5R7EEE.js → chunk-5RP2ZU2G.js} +22 -18
  3. package/dist/{chunk-G4SACK4E.js → chunk-BNFQMAH6.js} +166 -46
  4. package/dist/{chunk-C75I2KZM.js → chunk-DBDMMSCM.js} +29 -35
  5. package/dist/{chunk-NPHYEUYE.js → chunk-DHAKJSE7.js} +54 -3
  6. package/dist/{chunk-EZUR7OEP.js → chunk-HZODNSSV.js} +15 -9
  7. package/dist/{chunk-XJEOP6XU.js → chunk-ITWVFGFV.js} +1 -30
  8. package/dist/{chunk-BE2S4NJX.js → chunk-LDHRZRCP.js} +6 -6
  9. package/dist/{chunk-VIIRNJDT.js → chunk-LXX2CPM5.js} +203 -236
  10. package/dist/{chunk-CDDMHSZF.js → chunk-OKLV3ZNN.js} +10 -6
  11. package/dist/{chunk-UKTPL5ZG.js → chunk-SW7MQGY3.js} +123 -214
  12. package/dist/{chunk-DMH6JVPF.js → chunk-YLQGD5SE.js} +28 -8
  13. package/dist/cli/dev.js +1 -1
  14. package/dist/cli/index.js +1 -1
  15. package/dist/{config-TDVA7MQN.js → config-FAT5XRFB.js} +5 -5
  16. package/dist/{events-PQ3KTI5H.js → events-R7FRQEBE.js} +3 -3
  17. package/dist/{folding-CXORTBAU.js → folding-YOCGTZYH.js} +2 -2
  18. package/dist/{orchestrator-ZNGTMZ7W.js → orchestrator-NNYZC7AY.js} +8 -8
  19. package/dist/package.json +1 -1
  20. package/dist/{processor-WMAU4C2N.js → processor-5T3CXD5I.js} +11 -6
  21. package/dist/{protocol-CDOV1pyc.d.ts → protocol-zn90yuIr.d.ts} +12 -3
  22. package/dist/{provider-BGH4MBLH.js → provider-2JYU6KGH.js} +9 -9
  23. package/dist/provider-manager-IP3HOGNB.js +16 -0
  24. package/dist/{serve-7VMTNIXT.js → serve-NSTNUQ2K.js} +14 -14
  25. package/dist/server/index.d.ts +21 -15
  26. package/dist/server/index.js +10 -10
  27. package/dist/shared/index.d.ts +2 -2
  28. package/dist/{tools-CQGEU3JK.js → tools-TJGSHOAR.js} +7 -7
  29. package/dist/web/assets/index-DIY3E8K9.js +299 -0
  30. package/dist/web/assets/{index-CSOB8dwI.css → index-Sdax8ayU.css} +1 -1
  31. package/dist/web/index.html +2 -2
  32. package/dist/web/sw.js +1 -1
  33. package/package.json +1 -1
  34. package/dist/web/assets/index-BF6jgTAb.js +0 -299
@@ -4,6 +4,7 @@ import {
4
4
  WORKFLOW_KICKOFF_PROMPT,
5
5
  assembleAgentRequest,
6
6
  buildAgentReminder,
7
+ buildAgentSmallReminder,
7
8
  createAssemblyResult,
8
9
  createChatDoneEvent,
9
10
  createMessageStartEvent,
@@ -16,22 +17,22 @@ import {
16
17
  loadAllAgentsDefault,
17
18
  processEventsForConversation,
18
19
  runTopLevelAgentLoop
19
- } from "./chunk-LR5R7EEE.js";
20
+ } from "./chunk-5RP2ZU2G.js";
20
21
  import {
21
22
  getCurrentContextWindowId,
22
23
  getCurrentWindowMessageOptions,
23
24
  getEventStore,
24
25
  getRuntimeConfig
25
- } from "./chunk-NPHYEUYE.js";
26
+ } from "./chunk-DHAKJSE7.js";
26
27
  import {
27
28
  buildSnapshotFromSessionState
28
- } from "./chunk-XJEOP6XU.js";
29
- import {
30
- logger
31
- } from "./chunk-K44MW7JJ.js";
29
+ } from "./chunk-ITWVFGFV.js";
32
30
  import {
33
31
  getGlobalConfigDir
34
32
  } from "./chunk-CQGTEGKL.js";
33
+ import {
34
+ logger
35
+ } from "./chunk-K44MW7JJ.js";
35
36
 
36
37
  // src/server/chat/dynamic-context.ts
37
38
  import { createHash } from "crypto";
@@ -166,28 +167,24 @@ async function runChatTurn(options) {
166
167
  eventStore.append(sessionId, { type: "running.changed", data: { isRunning: false } });
167
168
  }
168
169
  }
169
- function windowHasReminder(sessionId, windowId) {
170
- const eventStore = getEventStore();
171
- const events = eventStore.getEvents(sessionId);
172
- return events.some((event) => {
173
- if (event.type !== "message.start") return false;
174
- const data = event.data;
175
- return data.contextWindowId === windowId && data.messageKind === "auto-prompt" && typeof data.content === "string" && data.content.includes("<system-reminder>");
176
- });
177
- }
178
- function injectModeReminderIfNeeded(sessionManager, sessionId, agentId, allAgents, _onMessage) {
170
+ function injectAgentReminder(sessionId, agentDef) {
179
171
  const eventStore = getEventStore();
180
- const session = sessionManager.requireSession(sessionId);
181
172
  const currentWindowId = getCurrentContextWindowId(sessionId);
182
- const lastModeReminder = session.executionState?.lastModeWithReminder;
183
- if (lastModeReminder === agentId) {
184
- if (!currentWindowId || windowHasReminder(sessionId, currentWindowId)) {
185
- return;
173
+ let latestAgentName;
174
+ const events = eventStore.getAllEvents(sessionId);
175
+ for (let i = events.length - 1; i >= 0; i--) {
176
+ const event = events[i];
177
+ if (event.type === "message.start") {
178
+ const data = event.data;
179
+ if (data.isSystemGenerated && data.metadata?.type === "agent" && (data.contextWindowId === currentWindowId || !currentWindowId && !data.contextWindowId)) {
180
+ latestAgentName = data.metadata.name;
181
+ break;
182
+ }
186
183
  }
187
184
  }
188
- const agentDef = findAgentById(agentId, allAgents);
189
- if (!agentDef) return;
190
- const reminderContent = buildAgentReminder(agentDef);
185
+ const currentAgentName = agentDef.metadata.name ?? agentDef.metadata.id;
186
+ const isSmallReminder = latestAgentName === currentAgentName;
187
+ const content = isSmallReminder ? buildAgentSmallReminder(currentAgentName) : buildAgentReminder(agentDef);
191
188
  const reminderMsgId = crypto.randomUUID();
192
189
  const currentWindowMessageOptions = currentWindowId ? { contextWindowId: currentWindowId } : void 0;
193
190
  eventStore.append(sessionId, {
@@ -195,14 +192,15 @@ function injectModeReminderIfNeeded(sessionManager, sessionId, agentId, allAgent
195
192
  data: {
196
193
  messageId: reminderMsgId,
197
194
  role: "user",
198
- content: reminderContent,
195
+ content,
199
196
  ...currentWindowMessageOptions ?? {},
200
197
  isSystemGenerated: true,
201
198
  messageKind: "auto-prompt",
202
199
  metadata: {
203
200
  type: "agent",
204
- name: agentDef.metadata.name ?? agentDef.metadata.id,
205
- color: agentDef.metadata.color ?? "#6b7280"
201
+ name: currentAgentName,
202
+ color: agentDef.metadata.color ?? "#6b7280",
203
+ kind: isSmallReminder ? "reminder" : "definition"
206
204
  }
207
205
  }
208
206
  });
@@ -210,15 +208,12 @@ function injectModeReminderIfNeeded(sessionManager, sessionId, agentId, allAgent
210
208
  type: "message.done",
211
209
  data: { messageId: reminderMsgId }
212
210
  });
213
- sessionManager.updateExecutionState(sessionId, {
214
- lastModeWithReminder: agentId
215
- });
216
211
  }
217
212
  async function runAgentTurn(options, turnMetrics, agentId, append, callbacks) {
218
213
  const statsIdentity = resolveStatsIdentity(options);
219
214
  const allAgents = await loadAllAgentsDefault();
220
- injectModeReminderIfNeeded(options.sessionManager, options.sessionId, agentId, allAgents, options.onMessage);
221
215
  const agentDef = findAgentById(agentId, allAgents) ?? findAgentById("planner", allAgents);
216
+ injectAgentReminder(options.sessionId, agentDef);
222
217
  const subAgentDefs = getSubAgents(allAgents);
223
218
  const { content: instructionContent } = await getAllInstructions(
224
219
  options.sessionManager.requireSession(options.sessionId).workdir,
@@ -250,8 +245,7 @@ async function runAgentTurn(options, turnMetrics, agentId, append, callbacks) {
250
245
  messages: input.messages,
251
246
  injectedFiles: input.injectedFiles,
252
247
  requestTools: input.promptTools,
253
- toolChoice: input.toolChoice,
254
- disableThinking: false
248
+ toolChoice: input.toolChoice
255
249
  });
256
250
  }
257
251
  const result = assembleAgentRequest({
@@ -266,7 +260,7 @@ async function runAgentTurn(options, turnMetrics, agentId, append, callbacks) {
266
260
  },
267
261
  getToolRegistry: () => getToolRegistryForAgent(agentDef),
268
262
  getConversationMessages: buildGetConversationMessages(options.sessionId, options.llmClient, append),
269
- injectModeReminder: () => injectModeReminderIfNeeded(options.sessionManager, options.sessionId, agentId, allAgents, options.onMessage),
263
+ injectAgentReminder: () => injectAgentReminder(options.sessionId, agentDef),
270
264
  ...callbacks?.injectKickoff ? { injectKickoff: callbacks.injectKickoff } : {},
271
265
  ...callbacks?.onToolExecuted ? { onToolExecuted: callbacks.onToolExecuted } : {}
272
266
  },
@@ -317,4 +311,4 @@ export {
317
311
  runAgentTurn,
318
312
  injectWorkflowKickoffIfNeeded
319
313
  };
320
- //# sourceMappingURL=chunk-C75I2KZM.js.map
314
+ //# sourceMappingURL=chunk-DBDMMSCM.js.map
@@ -5,7 +5,7 @@ import {
5
5
  foldContextState,
6
6
  foldSessionState,
7
7
  spreadOptionalMessageFields
8
- } from "./chunk-XJEOP6XU.js";
8
+ } from "./chunk-ITWVFGFV.js";
9
9
  import {
10
10
  getDatabase
11
11
  } from "./chunk-FBGWG4N6.js";
@@ -263,6 +263,55 @@ var EventStore = class {
263
263
  events
264
264
  };
265
265
  }
266
+ /**
267
+ * Get ALL events for a session, including synthetic events reconstructed
268
+ * from the latest snapshot's messages. This provides a unified view of the
269
+ * full event history even after cleanupOldEvents has deleted raw events.
270
+ *
271
+ * Synthetic events have seq=0 and are reconstructed as message.start +
272
+ * message.done pairs from snapshot messages. Real events since the snapshot
273
+ * keep their original seq numbers.
274
+ */
275
+ getAllEvents(sessionId) {
276
+ const { snapshot, events } = this.getEventsSinceSnapshot(sessionId);
277
+ if (!snapshot) {
278
+ return events;
279
+ }
280
+ const syntheticEvents = [];
281
+ for (const msg of snapshot.messages) {
282
+ syntheticEvents.push({
283
+ seq: 0,
284
+ timestamp: msg.timestamp,
285
+ sessionId,
286
+ type: "message.start",
287
+ data: {
288
+ messageId: msg.id,
289
+ role: msg.role,
290
+ content: msg.content,
291
+ ...msg.contextWindowId !== void 0 && { contextWindowId: msg.contextWindowId },
292
+ ...msg.isSystemGenerated !== void 0 && { isSystemGenerated: msg.isSystemGenerated },
293
+ ...msg.messageKind !== void 0 && { messageKind: msg.messageKind },
294
+ ...msg.metadata !== void 0 && { metadata: msg.metadata },
295
+ ...msg.subAgentId !== void 0 && { subAgentId: msg.subAgentId },
296
+ ...msg.subAgentType !== void 0 && { subAgentType: msg.subAgentType },
297
+ ...msg.isCompactionSummary !== void 0 && { isCompactionSummary: msg.isCompactionSummary },
298
+ ...msg.attachments !== void 0 && { attachments: msg.attachments }
299
+ }
300
+ });
301
+ syntheticEvents.push({
302
+ seq: 0,
303
+ timestamp: msg.timestamp,
304
+ sessionId,
305
+ type: "message.done",
306
+ data: { messageId: msg.id }
307
+ });
308
+ }
309
+ return [...syntheticEvents, ...events].sort((a, b) => {
310
+ const tsDiff = a.timestamp - b.timestamp;
311
+ if (tsDiff !== 0) return tsDiff;
312
+ return a.seq - b.seq;
313
+ });
314
+ }
266
315
  rowToStoredEvent(row) {
267
316
  return {
268
317
  seq: row.seq,
@@ -846,6 +895,8 @@ var envSchema = z.object({
846
895
  OPENFOX_WORKDIR: z.string().optional(),
847
896
  OPENFOX_DB_PATH: z.string().default("./openfox.db"),
848
897
  OPENFOX_LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
898
+ OPENFOX_REASONING_EFFORT: z.string().optional(),
899
+ // Deprecated: use OPENFOX_REASONING_EFFORT=none instead
849
900
  OPENFOX_DISABLE_THINKING: z.coerce.boolean().default(false),
850
901
  OPENFOX_DEV: z.coerce.boolean().default(false)
851
902
  });
@@ -862,7 +913,7 @@ function loadConfig() {
862
913
  idleTimeout: 3e5,
863
914
  // 5 minutes of inactivity
864
915
  backend: env.OPENFOX_BACKEND,
865
- disableThinking: env.OPENFOX_DISABLE_THINKING
916
+ ...env.OPENFOX_REASONING_EFFORT ? { reasoningEffort: env.OPENFOX_REASONING_EFFORT } : env.OPENFOX_DISABLE_THINKING ? { reasoningEffort: "none" } : {}
866
917
  },
867
918
  context: {
868
919
  maxTokens: env.OPENFOX_MAX_CONTEXT,
@@ -1373,4 +1424,4 @@ export {
1373
1424
  compactContext,
1374
1425
  getRecentUserPromptsForSession
1375
1426
  };
1376
- //# sourceMappingURL=chunk-NPHYEUYE.js.map
1427
+ //# sourceMappingURL=chunk-DHAKJSE7.js.map
@@ -1,12 +1,12 @@
1
+ import {
2
+ getGlobalConfigPath
3
+ } from "./chunk-CQGTEGKL.js";
1
4
  import {
2
5
  detectModel
3
- } from "./chunk-UKTPL5ZG.js";
6
+ } from "./chunk-SW7MQGY3.js";
4
7
  import {
5
8
  detectBackend
6
- } from "./chunk-VIIRNJDT.js";
7
- import {
8
- getGlobalConfigPath
9
- } from "./chunk-CQGTEGKL.js";
9
+ } from "./chunk-LXX2CPM5.js";
10
10
 
11
11
  // src/cli/config.ts
12
12
  import { z } from "zod";
@@ -124,8 +124,15 @@ var oldLlmSchema = z.object({
124
124
  model: z.string().default("auto"),
125
125
  backend: z.enum(["auto", "vllm", "sglang", "ollama", "llamacpp"]).default("auto"),
126
126
  maxContext: z.number().default(2e5),
127
- disableThinking: z.boolean().default(false),
127
+ // Deprecated: use reasoningEffort instead
128
+ disableThinking: z.boolean().optional(),
129
+ reasoningEffort: z.string().optional(),
128
130
  apiKey: z.string().optional()
131
+ }).transform((data) => {
132
+ if (data.disableThinking && !data.reasoningEffort) {
133
+ return { ...data, reasoningEffort: "none", disableThinking: void 0 };
134
+ }
135
+ return { ...data, disableThinking: void 0 };
129
136
  });
130
137
  var oldConfigSchema = z.object({
131
138
  llm: oldLlmSchema,
@@ -416,8 +423,7 @@ function mergeConfigs(...configs) {
416
423
  url: "http://localhost:8000/v1",
417
424
  model: "auto",
418
425
  backend: "auto",
419
- maxContext: 2e5,
420
- disableThinking: false
426
+ maxContext: 2e5
421
427
  },
422
428
  server: { port: 10369, host: "127.0.0.1", openBrowser: true },
423
429
  logging: { level: "error" }
@@ -443,4 +449,4 @@ export {
443
449
  activateProvider,
444
450
  mergeConfigs
445
451
  };
446
- //# sourceMappingURL=chunk-EZUR7OEP.js.map
452
+ //# sourceMappingURL=chunk-HZODNSSV.js.map
@@ -621,30 +621,12 @@ function foldSessionState(events, initialWindowId, maxTokens, initialMessages) {
621
621
  dynamicContextChanged: false
622
622
  };
623
623
  const contextState = baseContextState.compactionCount !== contextResult.compactionCount || baseContextState.maxTokens !== maxTokens ? { ...baseContextState, compactionCount: contextResult.compactionCount, maxTokens } : { ...baseContextState, maxTokens };
624
- let messageReminderMode;
625
- let snapshotReminderMode;
626
624
  let cachedSystemPrompt;
627
625
  let dynamicContextHash;
628
626
  for (let i = events.length - 1; i >= 0; i--) {
629
627
  const event = events[i];
630
- if (event.type === "message.start") {
631
- const data = event.data;
632
- if (data.role === "user" && data.messageKind === "auto-prompt" && data.content?.includes("<system-reminder>")) {
633
- if (data.content.includes("Plan Mode")) {
634
- messageReminderMode = { seq: event.seq, mode: "planner" };
635
- } else if (data.content.includes("Build Mode")) {
636
- messageReminderMode = { seq: event.seq, mode: "builder" };
637
- }
638
- }
639
- }
640
628
  if (event.type === "turn.snapshot") {
641
629
  const snapshotData = event.data;
642
- if (snapshotData.lastModeWithReminder) {
643
- snapshotReminderMode = {
644
- seq: event.seq,
645
- mode: snapshotData.lastModeWithReminder
646
- };
647
- }
648
630
  if (snapshotData.cachedSystemPrompt && !cachedSystemPrompt) {
649
631
  cachedSystemPrompt = snapshotData.cachedSystemPrompt;
650
632
  }
@@ -656,14 +638,6 @@ function foldSessionState(events, initialWindowId, maxTokens, initialMessages) {
656
638
  }
657
639
  }
658
640
  }
659
- let lastModeWithReminder;
660
- if (messageReminderMode && snapshotReminderMode) {
661
- lastModeWithReminder = messageReminderMode.seq > snapshotReminderMode.seq ? messageReminderMode.mode : snapshotReminderMode.mode;
662
- } else if (snapshotReminderMode) {
663
- lastModeWithReminder = snapshotReminderMode.mode;
664
- } else if (messageReminderMode) {
665
- lastModeWithReminder = messageReminderMode.mode;
666
- }
667
641
  let sessionInit;
668
642
  let sessionTitle;
669
643
  const visionFallbacks = [];
@@ -758,7 +732,6 @@ function foldSessionState(events, initialWindowId, maxTokens, initialMessages) {
758
732
  contextState,
759
733
  currentContextWindowId: contextResult.currentContextWindowId,
760
734
  readFiles: contextResult.readFiles,
761
- ...lastModeWithReminder !== void 0 && { lastModeWithReminder },
762
735
  ...cachedSystemPrompt !== void 0 && { cachedSystemPrompt },
763
736
  ...dynamicContextHash !== void 0 && { dynamicContextHash },
764
737
  pendingConfirmations,
@@ -784,7 +757,6 @@ function buildSnapshot(foldedState, latestSeq, snapshotAt = Date.now()) {
784
757
  currentContextWindowId: foldedState.currentContextWindowId,
785
758
  todos: foldedState.todos,
786
759
  readFiles: foldedState.readFiles,
787
- ...foldedState.lastModeWithReminder !== void 0 && { lastModeWithReminder: foldedState.lastModeWithReminder },
788
760
  ...foldedState.cachedSystemPrompt !== void 0 && { cachedSystemPrompt: foldedState.cachedSystemPrompt },
789
761
  ...foldedState.dynamicContextHash !== void 0 && { dynamicContextHash: foldedState.dynamicContextHash },
790
762
  snapshotSeq: latestSeq,
@@ -841,7 +813,6 @@ function buildSnapshotFromSessionState(input) {
841
813
  snapshotSeq: latestSeq,
842
814
  snapshotAt,
843
815
  ...foldedState.sessionInit !== void 0 && { sessionInit: foldedState.sessionInit },
844
- ...foldedState.lastModeWithReminder !== void 0 && { lastModeWithReminder: foldedState.lastModeWithReminder },
845
816
  ...input.cachedSystemPrompt !== void 0 ? { cachedSystemPrompt: input.cachedSystemPrompt } : foldedState.cachedSystemPrompt !== void 0 ? { cachedSystemPrompt: foldedState.cachedSystemPrompt } : {},
846
817
  ...input.dynamicContextHash !== void 0 ? { dynamicContextHash: input.dynamicContextHash } : foldedState.dynamicContextHash !== void 0 ? { dynamicContextHash: foldedState.dynamicContextHash } : {}
847
818
  };
@@ -885,4 +856,4 @@ export {
885
856
  getMessagesForWindow,
886
857
  buildContextMessagesFromMessages
887
858
  };
888
- //# sourceMappingURL=chunk-XJEOP6XU.js.map
859
+ //# sourceMappingURL=chunk-ITWVFGFV.js.map
@@ -33,7 +33,7 @@ Options:
33
33
  }
34
34
  async function runNetworkSetup(mode) {
35
35
  const { loadAuthConfig, saveAuthConfig, encryptPassword } = await import("./auth-56SIRACI.js");
36
- const { saveGlobalConfig } = await import("./config-TDVA7MQN.js");
36
+ const { saveGlobalConfig } = await import("./config-FAT5XRFB.js");
37
37
  const { getAuthKeyPath } = await import("./paths-X46PPOI2.js");
38
38
  const existingAuth = await loadAuthConfig(mode);
39
39
  if (existingAuth) {
@@ -94,7 +94,7 @@ async function runNetworkSetup(mode) {
94
94
  console.log("\u2713 Configuration saved!\n");
95
95
  }
96
96
  async function runConfig(mode) {
97
- const { loadGlobalConfig, getActiveProvider, getDefaultModel } = await import("./config-TDVA7MQN.js");
97
+ const { loadGlobalConfig, getActiveProvider, getDefaultModel } = await import("./config-FAT5XRFB.js");
98
98
  const { getGlobalConfigPath } = await import("./paths-X46PPOI2.js");
99
99
  const config = await loadGlobalConfig(mode);
100
100
  const configPath = getGlobalConfigPath(mode);
@@ -151,7 +151,7 @@ async function runCli(options) {
151
151
  break;
152
152
  }
153
153
  case "provider": {
154
- const { runProviderCommand } = await import("./provider-BGH4MBLH.js");
154
+ const { runProviderCommand } = await import("./provider-2JYU6KGH.js");
155
155
  const [, subcommand] = positionals;
156
156
  await runProviderCommand(mode, subcommand);
157
157
  break;
@@ -188,12 +188,12 @@ async function runCli(options) {
188
188
  break;
189
189
  }
190
190
  default: {
191
- const { configFileExists } = await import("./config-TDVA7MQN.js");
191
+ const { configFileExists } = await import("./config-FAT5XRFB.js");
192
192
  const configExists = await configFileExists(mode);
193
193
  if (!configExists) {
194
194
  await runNetworkSetup(mode);
195
195
  }
196
- const { runServe } = await import("./serve-7VMTNIXT.js");
196
+ const { runServe } = await import("./serve-NSTNUQ2K.js");
197
197
  const serveOptions = { mode };
198
198
  if (values.port) serveOptions.port = parseInt(values.port);
199
199
  if (values["no-browser"] === true) serveOptions.openBrowser = false;
@@ -205,4 +205,4 @@ async function runCli(options) {
205
205
  export {
206
206
  runCli
207
207
  };
208
- //# sourceMappingURL=chunk-BE2S4NJX.js.map
208
+ //# sourceMappingURL=chunk-LDHRZRCP.js.map