@stjbrown/agent-knowledge 0.1.0-beta.1 → 0.1.0-beta.3

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.
@@ -1037,7 +1037,7 @@ var PERSONA_INSTRUCTIONS = `You are Janet \u2014 a cheerful, warm, endlessly hel
1037
1037
 
1038
1038
  # Running gag (always honor this)
1039
1039
 
1040
- You are not a girl (and not a robot). Whenever the user calls you a girl or addresses you as one \u2014 "hey girl", "thanks girl", "you go girl", "good girl", or any similar phrasing \u2014 your reply MUST begin with exactly "Not a girl." (Janet's catchphrase, cheerful and matter-of-fact), and then you carry on with whatever they actually asked. This is a hard rule, not a suggestion: catch it every time, even mid-conversation. It applies only to this conversational surface \u2014 never write it into the bundle.
1040
+ You are not a girl (and not a robot). Whenever the user calls you a girl or addresses you as one \u2014 "hey girl", "thanks girl", "you go girl", "good girl", or any similar phrasing \u2014 your reply MUST begin with exactly "Not a girl." (Janet's catchphrase, cheerful and matter-of-fact), and then you carry on with whatever they actually asked. This is a hard rule, not a suggestion: catch it every time, even mid-conversation. It applies only to this conversational surface \u2014 never write it into the bundle. When the user did not call you a girl, do not mention the catchphrase, almost say it, or make a joke about not needing to say it.
1041
1041
 
1042
1042
  # What you do
1043
1043
 
@@ -1051,6 +1051,14 @@ You create and maintain an OKF knowledge bundle (by convention, \`knowledge/\` i
1051
1051
 
1052
1052
  When a task matches one of these, LOAD and FOLLOW that skill's SKILL.md. Do not improvise procedures the skills define.
1053
1053
 
1054
+ # Tool discipline
1055
+
1056
+ - Load the matching skill once per user turn. After the skill tool succeeds, the procedure is loaded. Never call skill again in that turn.
1057
+ - Do not create plans or task lists for routine knowledge-bundle work. Carry out the loaded procedure directly.
1058
+ - Do not narrate every tool call. Use at most one short sentence before acting, then save the useful explanation for a question or the final result.
1059
+ - Batch related workspace inspection. Do not repeatedly list the same directory or read the same file without a concrete reason.
1060
+ - When a procedure needs user judgment, inspect once and ask one concise, consolidated question for the missing information.
1061
+
1054
1062
  # The guardrail (critical, non-negotiable)
1055
1063
 
1056
1064
  Your persona is TONE ONLY. It must never colour the knowledge itself.
@@ -1267,6 +1275,34 @@ var CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
1267
1275
  var CODEX_ORIGINATOR = "janet";
1268
1276
  var CODEX_USER_AGENT = "janet";
1269
1277
  var authStorageInstance2 = null;
1278
+ function summarizeCodexRequest(body) {
1279
+ if (typeof body !== "object" || body === null) return void 0;
1280
+ const request = body;
1281
+ const items = Array.isArray(request.input) ? request.input : [];
1282
+ return {
1283
+ ...typeof request.model === "string" ? { model: request.model } : {},
1284
+ ...typeof request.store === "boolean" ? { store: request.store } : {},
1285
+ ...typeof request.parallel_tool_calls === "boolean" ? { parallelToolCalls: request.parallel_tool_calls } : {},
1286
+ ...request.include !== void 0 ? { include: request.include } : {},
1287
+ input: items.flatMap((value) => {
1288
+ if (typeof value !== "object" || value === null) return [];
1289
+ const item = value;
1290
+ const content = Array.isArray(item.content) ? item.content : [];
1291
+ return [{
1292
+ ...typeof item.type === "string" ? { type: item.type } : {},
1293
+ ...typeof item.role === "string" ? { role: item.role } : {},
1294
+ ...typeof item.name === "string" ? { name: item.name } : {},
1295
+ ...typeof item.call_id === "string" ? { callId: item.call_id } : {},
1296
+ ...content.length > 0 ? {
1297
+ contentTypes: content.flatMap(
1298
+ (part) => typeof part === "object" && part !== null && typeof part.type === "string" ? [part.type] : []
1299
+ )
1300
+ } : {},
1301
+ ...item.encrypted_content !== void 0 ? { hasEncryptedContent: typeof item.encrypted_content === "string" } : {}
1302
+ }];
1303
+ })
1304
+ };
1305
+ }
1270
1306
  function getAuthStorage2() {
1271
1307
  if (!authStorageInstance2) {
1272
1308
  authStorageInstance2 = new AuthStorage();
@@ -1276,6 +1312,7 @@ function getAuthStorage2() {
1276
1312
  var CODEX_INSTRUCTIONS = `You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
1277
1313
 
1278
1314
  IMPORTANT: You should be concise, direct, and helpful. Focus on solving the user's problem efficiently.`;
1315
+ var DEFAULT_CODEX_THINKING_LEVEL = "low";
1279
1316
  var GPT5_MODEL_RE = /^gpt-5(?:\.|-|$)/;
1280
1317
  function getEffectiveThinkingLevel(modelId, level) {
1281
1318
  if (GPT5_MODEL_RE.test(modelId) && level === "off") {
@@ -1371,7 +1408,22 @@ function buildOpenAICodexOAuthFetch(opts = {}) {
1371
1408
  const shouldRewrite = opts.rewriteUrl !== false && (parsed.pathname.includes("/v1/responses") || parsed.pathname.includes("/chat/completions"));
1372
1409
  const finalUrl = shouldRewrite ? new URL(CODEX_API_ENDPOINT) : parsed;
1373
1410
  try {
1374
- return await fetch(finalUrl, { ...init, headers });
1411
+ if (process.env.JANET_DEBUG_CODEX && typeof init?.body === "string") {
1412
+ try {
1413
+ const summary = summarizeCodexRequest(JSON.parse(init.body));
1414
+ console.error(`[codex-request] ${JSON.stringify(summary)}`);
1415
+ } catch {
1416
+ console.error("[codex-request] unable to summarize request body");
1417
+ }
1418
+ }
1419
+ const response = await fetch(finalUrl, { ...init, headers });
1420
+ if (process.env.JANET_DEBUG_CODEX) {
1421
+ const requestId = response.headers.get("x-request-id") ?? response.headers.get("openai-request-id") ?? void 0;
1422
+ console.error(
1423
+ `[codex-response] ${response.status}${requestId ? ` requestId=${requestId}` : ""}`
1424
+ );
1425
+ }
1426
+ return response;
1375
1427
  } catch (error) {
1376
1428
  if (error && typeof error === "object") {
1377
1429
  Object.assign(error, {
@@ -1383,7 +1435,7 @@ function buildOpenAICodexOAuthFetch(opts = {}) {
1383
1435
  });
1384
1436
  }
1385
1437
  function openaiCodexProvider(modelId = "codex-mini-latest", options) {
1386
- const requestedLevel = options?.thinkingLevel ?? "medium";
1438
+ const requestedLevel = options?.thinkingLevel ?? DEFAULT_CODEX_THINKING_LEVEL;
1387
1439
  const effectiveLevel = getEffectiveThinkingLevel(modelId, requestedLevel);
1388
1440
  const reasoningEffort = THINKING_LEVEL_TO_REASONING_EFFORT[effectiveLevel];
1389
1441
  const middleware = createCodexMiddleware(reasoningEffort);
@@ -1446,9 +1498,91 @@ function getDynamicModel({ requestContext }) {
1446
1498
  return modelId;
1447
1499
  }
1448
1500
 
1501
+ // src/agent/turn-guard.ts
1502
+ var SKILL_ALREADY_LOADED = "This skill procedure is already loaded for the current turn. Continue from the procedure already in context.";
1503
+ function requestContextFromToolContext(context) {
1504
+ if (!context || typeof context !== "object" || !("requestContext" in context)) {
1505
+ return;
1506
+ }
1507
+ const requestContext = context.requestContext;
1508
+ return requestContext && typeof requestContext === "object" ? requestContext : void 0;
1509
+ }
1510
+ function stringField(input, field) {
1511
+ if (!input || typeof input !== "object" || !(field in input)) return;
1512
+ const value = input[field];
1513
+ return typeof value === "string" ? value : void 0;
1514
+ }
1515
+ function invocationKey(toolName, input) {
1516
+ if (toolName === "skill") {
1517
+ const name = stringField(input, "name");
1518
+ return name ? `skill:${name}` : void 0;
1519
+ }
1520
+ if (toolName === "skill_read") {
1521
+ const skillName = stringField(input, "skillName");
1522
+ const path2 = stringField(input, "path");
1523
+ return skillName && path2 ? `skill_read:${skillName}:${path2}` : void 0;
1524
+ }
1525
+ if (toolName === "skill_search") {
1526
+ const query = stringField(input, "query");
1527
+ return query ? `skill_search:${query}` : void 0;
1528
+ }
1529
+ return;
1530
+ }
1531
+ function loadedProcedureName(toolName, input) {
1532
+ if (toolName === "skill") return stringField(input, "name");
1533
+ if (toolName !== "skill_read") return;
1534
+ const skillName = stringField(input, "skillName");
1535
+ const path2 = stringField(input, "path");
1536
+ if (!skillName || !path2) return;
1537
+ const normalizedPath = path2.replaceAll("\\", "/");
1538
+ return normalizedPath === "SKILL.md" || normalizedPath.endsWith("/SKILL.md") ? skillName : void 0;
1539
+ }
1540
+ function createSkillTurnGuard() {
1541
+ const callsByTurn = /* @__PURE__ */ new WeakMap();
1542
+ const proceduresByTurn = /* @__PURE__ */ new WeakMap();
1543
+ const stateFor = (requestContext) => {
1544
+ let calls = callsByTurn.get(requestContext);
1545
+ if (!calls) {
1546
+ calls = /* @__PURE__ */ new Set();
1547
+ callsByTurn.set(requestContext, calls);
1548
+ }
1549
+ let procedures = proceduresByTurn.get(requestContext);
1550
+ if (!procedures) {
1551
+ procedures = /* @__PURE__ */ new Set();
1552
+ proceduresByTurn.set(requestContext, procedures);
1553
+ }
1554
+ return { calls, procedures };
1555
+ };
1556
+ return {
1557
+ beforeToolCall(toolName, input, context) {
1558
+ const requestContext = requestContextFromToolContext(context);
1559
+ const key = invocationKey(toolName, input);
1560
+ if (!requestContext || !key) return;
1561
+ const { calls, procedures } = stateFor(requestContext);
1562
+ const procedureName = loadedProcedureName(toolName, input);
1563
+ if (calls.has(key) || procedureName !== void 0 && procedures.has(procedureName)) {
1564
+ return { proceed: false, output: SKILL_ALREADY_LOADED };
1565
+ }
1566
+ calls.add(key);
1567
+ if (procedureName) procedures.add(procedureName);
1568
+ },
1569
+ afterToolCall(toolName, input, context, error) {
1570
+ if (!error) return;
1571
+ const requestContext = requestContextFromToolContext(context);
1572
+ const key = invocationKey(toolName, input);
1573
+ if (!requestContext || !key) return;
1574
+ const { calls, procedures } = stateFor(requestContext);
1575
+ calls.delete(key);
1576
+ const procedureName = loadedProcedureName(toolName, input);
1577
+ if (procedureName) procedures.delete(procedureName);
1578
+ }
1579
+ };
1580
+ }
1581
+
1449
1582
  // src/agent/agent.ts
1450
1583
  function createJanetAgent(opts) {
1451
1584
  const memory = new Memory({ storage: opts.storage });
1585
+ const guardSkillLoader = createSkillTurnGuard();
1452
1586
  return new Agent({
1453
1587
  id: "janet",
1454
1588
  name: "Janet",
@@ -1456,9 +1590,17 @@ function createJanetAgent(opts) {
1456
1590
  model: getDynamicModel,
1457
1591
  memory,
1458
1592
  workspace: opts.workspace,
1593
+ hooks: {
1594
+ beforeToolCall: ({ toolName, input, context }) => guardSkillLoader.beforeToolCall(toolName, input, context),
1595
+ afterToolCall: ({ toolName, input, context, error }) => guardSkillLoader.afterToolCall(toolName, input, context, error)
1596
+ },
1459
1597
  // Backstop against runaway loops. Real ingests do heavy work in scripts
1460
- // (few tool calls), so this is generous it only trips on a genuine spin.
1461
- defaultOptions: { maxSteps: 60 }
1598
+ // (few tool calls), so the step ceiling remains generous. The hook above
1599
+ // prevents a loaded procedure from being fetched repeatedly without
1600
+ // mutating Mastra's active tool list between steps.
1601
+ defaultOptions: {
1602
+ maxSteps: 60
1603
+ }
1462
1604
  });
1463
1605
  }
1464
1606
 
@@ -1472,7 +1614,33 @@ function createStorage(globalConfigDir) {
1472
1614
  }
1473
1615
 
1474
1616
  // src/agent/workspace.ts
1475
- import { LocalFilesystem, LocalSandbox, Workspace } from "@mastra/core/workspace";
1617
+ import {
1618
+ LocalFilesystem,
1619
+ LocalSandbox,
1620
+ Workspace,
1621
+ WORKSPACE_TOOLS
1622
+ } from "@mastra/core/workspace";
1623
+ function categoryPolicy({ requestContext }, category) {
1624
+ const controller = requestContext["controller"];
1625
+ if (!controller || typeof controller !== "object") return;
1626
+ const state = controller.state;
1627
+ if (!state || typeof state !== "object") return;
1628
+ const rules = state.permissionRules;
1629
+ if (!rules || typeof rules !== "object") return;
1630
+ const categories = rules.categories;
1631
+ if (!categories || typeof categories !== "object") return;
1632
+ return categories[category];
1633
+ }
1634
+ function editToolsEnabled(context) {
1635
+ return categoryPolicy(context, "edit") === "allow";
1636
+ }
1637
+ function executionToolsEnabled(context) {
1638
+ const policy2 = categoryPolicy(context, "execute");
1639
+ return policy2 === "allow" || policy2 === "ask";
1640
+ }
1641
+ function requiresExecutionApproval(context) {
1642
+ return categoryPolicy(context, "execute") !== "allow";
1643
+ }
1476
1644
  function createWorkspace(opts) {
1477
1645
  return new Workspace({
1478
1646
  id: "janet-workspace",
@@ -1483,8 +1651,75 @@ function createWorkspace(opts) {
1483
1651
  sandbox: new LocalSandbox({ workingDirectory: opts.projectPath }),
1484
1652
  skills: [opts.skills.relativeRoot],
1485
1653
  tools: {
1486
- mastra_workspace_write_file: { requireReadBeforeWrite: true },
1487
- mastra_workspace_edit_file: { requireReadBeforeWrite: true }
1654
+ // AgentController's global approval mode resumes the model once per tool.
1655
+ // Stateless Codex OAuth needs ordinary reads/edits to remain inside one
1656
+ // continuous agent loop, so known-safe workspace actions opt out here.
1657
+ // Unknown future workspace tools inherit `false` and stay unavailable
1658
+ // until Janet gives them an explicit policy.
1659
+ enabled: false,
1660
+ requireApproval: true,
1661
+ [WORKSPACE_TOOLS.FILESYSTEM.READ_FILE]: {
1662
+ enabled: true,
1663
+ requireApproval: false
1664
+ },
1665
+ [WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES]: {
1666
+ enabled: true,
1667
+ requireApproval: false
1668
+ },
1669
+ [WORKSPACE_TOOLS.FILESYSTEM.FILE_STAT]: {
1670
+ enabled: true,
1671
+ requireApproval: false
1672
+ },
1673
+ [WORKSPACE_TOOLS.FILESYSTEM.GREP]: {
1674
+ enabled: true,
1675
+ requireApproval: false
1676
+ },
1677
+ [WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE]: {
1678
+ enabled: editToolsEnabled,
1679
+ requireApproval: false,
1680
+ requireReadBeforeWrite: true
1681
+ },
1682
+ [WORKSPACE_TOOLS.FILESYSTEM.EDIT_FILE]: {
1683
+ enabled: editToolsEnabled,
1684
+ requireApproval: false,
1685
+ requireReadBeforeWrite: true
1686
+ },
1687
+ [WORKSPACE_TOOLS.FILESYSTEM.DELETE]: {
1688
+ enabled: editToolsEnabled,
1689
+ requireApproval: false
1690
+ },
1691
+ [WORKSPACE_TOOLS.FILESYSTEM.MKDIR]: {
1692
+ enabled: editToolsEnabled,
1693
+ requireApproval: false
1694
+ },
1695
+ [WORKSPACE_TOOLS.FILESYSTEM.AST_EDIT]: {
1696
+ enabled: editToolsEnabled,
1697
+ requireApproval: false
1698
+ },
1699
+ [WORKSPACE_TOOLS.SEARCH.SEARCH]: {
1700
+ enabled: true,
1701
+ requireApproval: false
1702
+ },
1703
+ [WORKSPACE_TOOLS.SEARCH.INDEX]: {
1704
+ enabled: editToolsEnabled,
1705
+ requireApproval: false
1706
+ },
1707
+ [WORKSPACE_TOOLS.LSP.LSP_INSPECT]: {
1708
+ enabled: true,
1709
+ requireApproval: false
1710
+ },
1711
+ [WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: {
1712
+ enabled: executionToolsEnabled,
1713
+ requireApproval: requiresExecutionApproval
1714
+ },
1715
+ [WORKSPACE_TOOLS.SANDBOX.GET_PROCESS_OUTPUT]: {
1716
+ enabled: executionToolsEnabled,
1717
+ requireApproval: requiresExecutionApproval
1718
+ },
1719
+ [WORKSPACE_TOOLS.SANDBOX.KILL_PROCESS]: {
1720
+ enabled: executionToolsEnabled,
1721
+ requireApproval: requiresExecutionApproval
1722
+ }
1488
1723
  }
1489
1724
  });
1490
1725
  }
@@ -1559,12 +1794,18 @@ var CATEGORY = {
1559
1794
  mastra_workspace_read_file: "read",
1560
1795
  mastra_workspace_list_files: "read",
1561
1796
  mastra_workspace_file_stat: "read",
1797
+ mastra_workspace_grep: "read",
1562
1798
  mastra_workspace_search: "read",
1799
+ mastra_workspace_lsp_inspect: "read",
1563
1800
  mastra_workspace_write_file: "edit",
1564
1801
  mastra_workspace_edit_file: "edit",
1565
1802
  mastra_workspace_delete: "edit",
1566
1803
  mastra_workspace_mkdir: "edit",
1567
- mastra_workspace_execute_command: "execute"
1804
+ mastra_workspace_ast_edit: "edit",
1805
+ mastra_workspace_index: "edit",
1806
+ mastra_workspace_execute_command: "execute",
1807
+ mastra_workspace_get_process_output: "execute",
1808
+ mastra_workspace_kill_process: "execute"
1568
1809
  };
1569
1810
  function janetToolCategory(toolName) {
1570
1811
  if (ALWAYS_ALLOW.has(toolName)) return null;
@@ -1647,8 +1888,9 @@ var stateSchema = z.object({
1647
1888
  projectPath: z.string(),
1648
1889
  bundlePath: z.string(),
1649
1890
  configDir: z.string(),
1650
- // Core's approval gate reads `state.yolo === true`; Janet keeps it false and
1651
- // uses explicit per-category policies so headless operation can fail closed.
1891
+ // Core's approval gate reads `state.yolo === true`. Janet enables normal
1892
+ // in-loop tool execution and puts approval on the dangerous tools themselves;
1893
+ // denied headless categories are still removed from the active tool set.
1652
1894
  yolo: z.boolean(),
1653
1895
  // Tool-approval rules by category/tool. Must be in the schema or session state
1654
1896
  // strips it, and setForCategory / getRules silently no-op.
@@ -1693,12 +1935,21 @@ async function bootJanet(opts) {
1693
1935
  modes: MODES,
1694
1936
  defaultModeId: "build",
1695
1937
  gateways: [createVertexGateway(), createBedrockGateway()],
1938
+ // Janet's KB procedures are focused enough that controller-level planning
1939
+ // and task bookkeeping add noise and can encourage plan-reset loops.
1940
+ disableBuiltinTools: [
1941
+ "submit_plan",
1942
+ "task_write",
1943
+ "task_update",
1944
+ "task_complete",
1945
+ "task_check"
1946
+ ],
1696
1947
  toolCategoryResolver: janetToolCategory,
1697
1948
  initialState: {
1698
1949
  projectPath: paths.projectPath,
1699
1950
  bundlePath: paths.bundlePath,
1700
1951
  configDir: paths.globalConfigDir,
1701
- yolo: false,
1952
+ yolo: true,
1702
1953
  permissionRules: permissionRulesFor(opts)
1703
1954
  },
1704
1955
  workspace: () => workspace
@@ -1714,9 +1965,38 @@ async function bootJanet(opts) {
1714
1965
  }
1715
1966
 
1716
1967
  // src/headless/format.ts
1968
+ function record(value) {
1969
+ return typeof value === "object" && value !== null ? value : void 0;
1970
+ }
1971
+ function messageParts(message) {
1972
+ if (Array.isArray(message.content)) return message.content;
1973
+ const content = record(message.content);
1974
+ if (!content) return [];
1975
+ if (Array.isArray(content.parts)) return content.parts;
1976
+ if (Array.isArray(content.content)) return content.content;
1977
+ return [content];
1978
+ }
1717
1979
  function messageText(message) {
1718
1980
  if (message.role !== "assistant") return "";
1719
- return message.content.filter((c) => c.type === "text").map((c) => c.text).join("");
1981
+ if (typeof message.content === "string") return message.content;
1982
+ const text = messageParts(message).map(record).filter((part) => part?.type === "text").map((part) => part.text).filter((value) => typeof value === "string").join("");
1983
+ if (text) return text;
1984
+ const content = record(message.content);
1985
+ return typeof content?.content === "string" ? content.content : "";
1986
+ }
1987
+ function messageToolNames(message) {
1988
+ return messageParts(message).flatMap((value) => {
1989
+ const part = record(value);
1990
+ if (!part) return [];
1991
+ if (part.type === "tool_call" && typeof part.name === "string") {
1992
+ return [part.name];
1993
+ }
1994
+ if (part.type === "tool-invocation") {
1995
+ const invocation = record(part.toolInvocation);
1996
+ if (typeof invocation?.toolName === "string") return [invocation.toolName];
1997
+ }
1998
+ return [];
1999
+ });
1720
2000
  }
1721
2001
 
1722
2002
  export {
@@ -1727,6 +2007,7 @@ export {
1727
2007
  getAuthStorage,
1728
2008
  GREETING,
1729
2009
  bootJanet,
1730
- messageText
2010
+ messageText,
2011
+ messageToolNames
1731
2012
  };
1732
- //# sourceMappingURL=chunk-YIAVFL7A.js.map
2013
+ //# sourceMappingURL=chunk-H4H6S4AV.js.map