codiedev 0.10.0-beta.5 → 0.10.0-beta.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.
Files changed (44) hide show
  1. package/README.md +4 -4
  2. package/dist/captureSignals.js +2 -0
  3. package/dist/cli.js +38 -50
  4. package/dist/codexHookTrust.js +67 -4
  5. package/dist/codie.js +23 -74
  6. package/dist/codieLauncher.js +18 -22
  7. package/dist/commands/agentTemplates.generated.js +136 -0
  8. package/dist/commands/agents.js +1068 -0
  9. package/dist/commands/agentsSdk.generated.js +7 -0
  10. package/dist/commands/doctor.js +12 -38
  11. package/dist/commands/evals.js +187 -0
  12. package/dist/commands/pack.js +1 -1
  13. package/dist/commands/traces.js +79 -15
  14. package/dist/commands/tracesLabel.js +348 -0
  15. package/dist/connect.js +11 -52
  16. package/dist/copilotHooks.js +0 -4
  17. package/dist/cursorHooks.js +0 -15
  18. package/dist/harnessDirs.js +4 -0
  19. package/dist/hook.js +33 -197
  20. package/dist/limitEventReporter.js +1 -1
  21. package/dist/managedPi.js +240 -0
  22. package/dist/mcp.js +0 -307
  23. package/dist/packs/harnesses.js +40 -3
  24. package/dist/packs/install.js +2 -1
  25. package/dist/piHooks.js +216 -0
  26. package/dist/piRuntime.js +120 -0
  27. package/dist/selfUpdate.js +47 -2
  28. package/dist/utils.js +232 -115
  29. package/package.json +5 -7
  30. package/dist/codieStartup.js +0 -27
  31. package/dist/commands/promote.js +0 -53
  32. package/dist/commands/react.js +0 -38
  33. package/dist/commands/reverseTicket.js +0 -444
  34. package/dist/commands/security.js +0 -156
  35. package/dist/prUrl.js +0 -28
  36. package/dist/security.js +0 -417
  37. package/dist/securityDeepScan.js +0 -235
  38. package/dist/securityDoctor.js +0 -61
  39. package/dist/securityFeature.js +0 -6
  40. package/dist/securityInstall.js +0 -232
  41. package/dist/securityInventory.js +0 -566
  42. package/dist/securityPilot.js +0 -59
  43. package/dist/securityPolicy.js +0 -181
  44. package/dist/securitySession.js +0 -72
package/dist/hook.js CHANGED
@@ -53,15 +53,11 @@ const selfUpdate_1 = require("./selfUpdate");
53
53
  const version_1 = require("./version");
54
54
  const sync_1 = require("./packs/sync");
55
55
  const captureLog_1 = require("./captureLog");
56
- const security_1 = require("./security");
57
- const securitySession_1 = require("./securitySession");
58
- const securityPolicy_1 = require("./securityPolicy");
59
56
  const subagents_1 = require("./subagents");
60
- const securityFeature_1 = require("./securityFeature");
61
- const securityInventory_1 = require("./securityInventory");
62
57
  const captureExclusions_1 = require("./captureExclusions");
63
58
  const cursorHooks_1 = require("./cursorHooks");
64
59
  const copilotHooks_1 = require("./copilotHooks");
60
+ const piHooks_1 = require("./piHooks");
65
61
  function buildDiscoveryContext(skills) {
66
62
  if (skills.length === 0)
67
63
  return null;
@@ -321,131 +317,6 @@ function postJsonForResponse(url, body, bearerToken, timeoutMs) {
321
317
  req.end();
322
318
  });
323
319
  }
324
- async function runSecurityHook(hookInput, phase, source, uploadTelemetry = true) {
325
- const config = (0, utils_1.readConfig)();
326
- if (!config || !(0, securityFeature_1.isAgentSecurityEnabled)(config))
327
- return false;
328
- const policy = (0, securityPolicy_1.readEndpointSecurityPolicy)(undefined, undefined, config.companyId);
329
- const sessionId = hookInput.session_id ?? hookInput.sessionId;
330
- const sessionSignal = phase === "pre" ? (0, securitySession_1.readSessionSecuritySignal)(sessionId) : undefined;
331
- const localDecision = (0, security_1.evaluateToolSecurity)(hookInput, phase, policy, source, sessionSignal);
332
- if (phase === "post" && (0, security_1.securityInputHasSensitivePath)(hookInput)) {
333
- (0, securitySession_1.recordSensitiveReadSignal)(sessionId);
334
- }
335
- const event = (0, security_1.buildSecurityWorkflowEvent)(hookInput, phase, source);
336
- if (event) {
337
- event.localDecision = localDecision.action;
338
- event.localCategory = localDecision.category;
339
- }
340
- if (localDecision.action === "block" && phase === "pre") {
341
- (0, captureLog_1.appendCaptureLog)({
342
- stage: "security",
343
- sessionId: hookInput.session_id ?? hookInput.sessionId ?? "unknown",
344
- outcome: "ok",
345
- detail: `phase=${phase} localDecision=block category=${localDecision.category}`,
346
- });
347
- if (uploadTelemetry)
348
- dispatchSecurityEventUpload(event);
349
- const response = source === "cursor"
350
- ? (0, cursorHooks_1.cursorPreToolBlockResponse)(localDecision.reason ?? "Codie blocked this action")
351
- : source === "copilot"
352
- ? (0, copilotHooks_1.copilotPreToolBlockResponse)(localDecision.reason ?? "Codie blocked this action")
353
- : {
354
- hookSpecificOutput: {
355
- hookEventName: "PreToolUse",
356
- permissionDecision: "deny",
357
- permissionDecisionReason: localDecision.reason,
358
- },
359
- };
360
- process.stdout.write(JSON.stringify(response));
361
- return true;
362
- }
363
- if (event && uploadTelemetry)
364
- dispatchSecurityEventUpload(event);
365
- return false;
366
- }
367
- function runPromptSecurityHook(hookInput, source, uploadTelemetry = true) {
368
- const config = (0, utils_1.readConfig)();
369
- if (!config || !(0, securityFeature_1.isAgentSecurityEnabled)(config))
370
- return false;
371
- const decision = (0, security_1.evaluatePromptSecurity)(hookInput.prompt, (0, securityPolicy_1.readEndpointSecurityPolicy)(undefined, undefined, config.companyId), source);
372
- if (decision.action !== "block")
373
- return false;
374
- (0, captureLog_1.appendCaptureLog)({
375
- stage: "security",
376
- sessionId: hookInput.session_id ?? hookInput.sessionId ?? "unknown",
377
- outcome: "ok",
378
- detail: `phase=prompt localDecision=block category=${decision.category} telemetry=${uploadTelemetry ? "enabled" : "suppressed"} cwd=${hookInput.cwd ?? "unknown"}`,
379
- });
380
- if (uploadTelemetry) {
381
- dispatchSecurityEventUpload((0, security_1.buildBlockedSecurityPromptEvent)(hookInput, source, decision.category ?? "secret_in_prompt"));
382
- }
383
- if (source !== "copilot") {
384
- const response = source === "cursor"
385
- ? (0, cursorHooks_1.cursorPromptBlockResponse)(decision.reason ?? "Codie blocked this prompt")
386
- : { decision: "block", reason: decision.reason };
387
- process.stdout.write(JSON.stringify(response));
388
- }
389
- return true;
390
- }
391
- function dispatchAllowedPromptSecurityEvent(hookInput, source) {
392
- const config = (0, utils_1.readConfig)();
393
- if (!config || !(0, securityFeature_1.isAgentSecurityEnabled)(config))
394
- return;
395
- const event = (0, security_1.buildSecurityPromptEvent)(hookInput, source, Date.now, (0, securityPolicy_1.readEndpointSecurityPolicy)(undefined, undefined, config.companyId));
396
- if (event)
397
- dispatchSecurityEventUpload(event);
398
- }
399
- function dispatchSecurityEventUpload(event) {
400
- if (!event || !(0, securityFeature_1.isAgentSecurityEnabled)((0, utils_1.readConfig)()))
401
- return;
402
- try {
403
- const child = (0, child_process_1.spawn)(process.execPath, [process.argv[1], "security-upload"], {
404
- detached: true,
405
- stdio: ["pipe", "ignore", "ignore"],
406
- });
407
- child.stdin?.end(JSON.stringify(event));
408
- child.unref();
409
- }
410
- catch {
411
- }
412
- }
413
- async function runSecurityEventUpload() {
414
- const config = (0, utils_1.readConfig)();
415
- if (!config || !(0, securityFeature_1.isAgentSecurityEnabled)(config))
416
- return;
417
- const startedAt = Date.now();
418
- try {
419
- const raw = await readStdin();
420
- const event = JSON.parse(raw);
421
- const cachedPolicy = (0, securityPolicy_1.readEndpointSecurityPolicy)(undefined, undefined, config.companyId);
422
- const eventWithEndpoint = (0, security_1.withEndpointIdentity)(event, {
423
- endpointId: config.deviceId,
424
- endpointName: config.deviceName,
425
- cliVersion: version_1.CLI_VERSION,
426
- securityRuntimeVersion: security_1.SECURITY_RUNTIME_VERSION,
427
- securityPolicyVersion: cachedPolicy?.policyVersion,
428
- });
429
- const result = await postJsonForResponse(`${config.backendUrl}/api/cli/securityEvent`, { ...eventWithEndpoint }, config.token, 5_000);
430
- if (result?.policy?.schemaVersion === 1) {
431
- (0, securityPolicy_1.writeEndpointSecurityPolicy)(result.policy, undefined, undefined, config.companyId);
432
- }
433
- (0, captureLog_1.appendCaptureLog)({
434
- stage: "security",
435
- sessionId: event.sessionId,
436
- outcome: "ok",
437
- detail: `phase=${event.phase} elapsedMs=${Date.now() - startedAt} verdict=${String(result?.verdict ?? "unknown")}`,
438
- });
439
- }
440
- catch (err) {
441
- (0, captureLog_1.appendCaptureLog)({
442
- stage: "security",
443
- sessionId: "upload",
444
- outcome: "ERROR",
445
- detail: `elapsedMs=${Date.now() - startedAt} ${err.message}`,
446
- });
447
- }
448
- }
449
320
  function postBytes(url, bytes, contentType) {
450
321
  return new Promise((resolve, reject) => {
451
322
  const parsed = new URL(url);
@@ -502,6 +373,8 @@ function resolveMode(arg) {
502
373
  return "copilot";
503
374
  if (arg === "capture-opencode" || arg === "capture-opencode-final")
504
375
  return "opencode";
376
+ if (arg === "capture-pi" || arg === "capture-pi-final")
377
+ return "pi";
505
378
  if (arg === "capture-desktop" || arg === "capture-desktop-final")
506
379
  return "desktop";
507
380
  return "claude-code";
@@ -542,17 +415,6 @@ async function main() {
542
415
  }
543
416
  process.exit(0);
544
417
  }
545
- if (process.argv[2] === "security-inventory-upload") {
546
- const config = (0, utils_1.readConfig)();
547
- if (config && (0, securityFeature_1.isAgentSecurityEnabled)(config)) {
548
- const inventory = (0, securityInventory_1.buildSecurityInventory)({ workspace: process.argv[3] || process.cwd() });
549
- try {
550
- await (0, securityInventory_1.uploadSecurityInventory)(inventory, config);
551
- }
552
- catch { }
553
- }
554
- process.exit(0);
555
- }
556
418
  if (process.argv[2] === "backup-upload") {
557
419
  const { runBackupUpload } = await Promise.resolve().then(() => __importStar(require("./backup/run")));
558
420
  await runBackupUpload(process.argv[3]);
@@ -577,10 +439,6 @@ async function main() {
577
439
  }
578
440
  process.exit(0);
579
441
  }
580
- if (process.argv[2] === "security-upload") {
581
- await runSecurityEventUpload();
582
- process.exit(0);
583
- }
584
442
  if (process.argv[2] === "self-update") {
585
443
  await (0, selfUpdate_1.runSelfUpdate)(process.argv[1]);
586
444
  process.exit(0);
@@ -588,6 +446,7 @@ async function main() {
588
446
  const migrated = process.env.CODIEDEV_VIA_LAUNCHER ? [] : (0, utils_1.migrateHooksToLauncher)();
589
447
  if (migrated.length === 0)
590
448
  (0, selfUpdate_1.maybeSpawnSelfUpdate)(process.argv[1]);
449
+ (0, selfUpdate_1.maybeRefreshInstructions)();
591
450
  let hookInput = {};
592
451
  try {
593
452
  const raw = await readStdin();
@@ -603,54 +462,26 @@ async function main() {
603
462
  catch {
604
463
  process.exit(0);
605
464
  }
606
- const captureIgnored = (0, captureExclusions_1.shouldIgnoreCaptureInput)((0, utils_1.readConfig)(), hookInput);
607
- const securityCommand = process.argv[2] ?? "";
608
- const securityMatch = securityCommand.match(/^security-(pre|post|failure)(?:-(codex|cursor|codie|copilot))?$/);
609
- if (securityMatch) {
610
- const source = securityMatch[2] === "codex"
611
- ? "codex"
612
- : securityMatch[2] === "cursor"
613
- ? "cursor"
614
- : securityMatch[2] === "codie"
615
- ? "codie-harness"
616
- : securityMatch[2] === "copilot"
617
- ? "copilot"
618
- : "claude-code";
619
- const calls = hookInput.copilotToolCalls;
620
- const inputs = calls?.length
621
- ? calls.map((call) => ({ ...hookInput, tool_name: call.name, tool_input: call.input, tool_use_id: call.id }))
622
- : [hookInput];
623
- for (const input of inputs) {
624
- if (await runSecurityHook(input, securityMatch[1], source, !captureIgnored))
625
- break;
626
- }
465
+ if (process.argv[2]?.startsWith("security-"))
627
466
  process.exit(0);
628
- }
467
+ const captureIgnored = (0, captureExclusions_1.shouldIgnoreCaptureInput)((0, utils_1.readConfig)(), hookInput);
629
468
  const promptCommand = process.argv[2] ?? "";
630
- if (promptCommand === "security-prompt-cursor") {
631
- if (runPromptSecurityHook(hookInput, "cursor", !captureIgnored))
632
- return;
633
- if (captureIgnored)
634
- return;
635
- dispatchAllowedPromptSecurityEvent(hookInput, "cursor");
636
- process.exit(0);
637
- }
638
469
  if (process.argv[2] === "discover" ||
639
470
  process.argv[2] === "discover-codex" ||
640
471
  process.argv[2] === "discover-codie" ||
641
- process.argv[2] === "discover-copilot") {
472
+ process.argv[2] === "discover-copilot" ||
473
+ process.argv[2] === "discover-pi") {
642
474
  const source = promptCommand === "discover-codex"
643
475
  ? "codex"
644
476
  : promptCommand === "discover-codie"
645
477
  ? "codie-harness"
646
478
  : promptCommand === "discover-copilot"
647
479
  ? "copilot"
648
- : "claude-code";
649
- if (runPromptSecurityHook(hookInput, source, !captureIgnored))
650
- return;
480
+ : promptCommand === "discover-pi"
481
+ ? "pi"
482
+ : "claude-code";
651
483
  if (captureIgnored)
652
484
  return;
653
- dispatchAllowedPromptSecurityEvent(hookInput, source);
654
485
  if (source === "codex")
655
486
  await runCodexLimitSweep(hookInput.session_id);
656
487
  if (source === "codex")
@@ -693,19 +524,10 @@ async function main() {
693
524
  if (process.argv[2] === "session-start" ||
694
525
  process.argv[2] === "session-start-cursor" ||
695
526
  process.argv[2] === "session-start-copilot" ||
696
- process.argv[2] === "session-start-opencode") {
697
- if ((0, securityFeature_1.isAgentSecurityEnabled)((0, utils_1.readConfig)()) && (0, securityInventory_1.securityInventoryDue)()) {
698
- try {
699
- const child = (0, child_process_1.spawn)(process.execPath, [process.argv[1], "security-inventory-upload", hookInput.cwd || process.cwd()], {
700
- detached: true,
701
- stdio: "ignore",
702
- });
703
- child.unref();
704
- }
705
- catch { }
706
- }
527
+ process.argv[2] === "session-start-opencode" ||
528
+ process.argv[2] === "session-start-pi") {
707
529
  (0, sync_1.maybeSpawnPackSync)({ connected: Boolean((0, utils_1.readConfig)()), cli: path.join(__dirname, "cli.js") });
708
- const packNag = process.argv[2] === "session-start-opencode" ? null : (0, sync_1.packNagLine)();
530
+ const packNag = process.argv[2] === "session-start-opencode" || process.argv[2] === "session-start-pi" ? null : (0, sync_1.packNagLine)();
709
531
  if (packNag && process.argv[2] === "session-start")
710
532
  process.stdout.write(`${packNag}\n`);
711
533
  await runSessionStart(hookInput, process.argv[2] === "session-start-opencode"
@@ -714,7 +536,9 @@ async function main() {
714
536
  ? "cursor"
715
537
  : process.argv[2] === "session-start-copilot"
716
538
  ? "copilot"
717
- : "claude-code");
539
+ : process.argv[2] === "session-start-pi"
540
+ ? "pi"
541
+ : "claude-code");
718
542
  if ((0, utils_1.readConfig)() && (process.argv[2] === "session-start-cursor" || process.argv[2] === "session-start-copilot")) {
719
543
  const source = process.argv[2] === "session-start-cursor" ? "cursor" : "copilot";
720
544
  const { TEAM_SKILL_CONTEXT, teamSkillSessionOutput } = await Promise.resolve().then(() => __importStar(require("./teamSkillContext")));
@@ -731,11 +555,16 @@ async function main() {
731
555
  else if ((process.argv[2] ?? "").endsWith("-copilot")) {
732
556
  (0, utils_1.ensureCopilotCodiedevHooksInstalled)();
733
557
  }
558
+ else if (/-pi(?:-final)?$/.test(process.argv[2] ?? "")) {
559
+ (0, utils_1.ensurePiExtensionInstalled)();
560
+ }
734
561
  else {
735
562
  (0, utils_1.ensureAllCodiedevHooksInstalled)();
736
563
  }
737
564
  if (process.argv[2] === "capture-codex")
738
565
  (0, utils_1.ensureCodexLimitHookInstalled)();
566
+ if (/-opencode(?:-final)?$/.test(process.argv[2] ?? ""))
567
+ (0, utils_1.ensureOpenCodePluginCurrent)();
739
568
  const { session_id, transcript_path, cwd, workspace_roots } = hookInput;
740
569
  const launch = (0, launchContext_1.hookLaunch)(process.argv[2], hookInput, {
741
570
  copilotClientName: process.argv[2]?.endsWith("-copilot")
@@ -745,7 +574,7 @@ async function main() {
745
574
  const workspaceRoot = workspace_roots && workspace_roots.length > 0
746
575
  ? workspace_roots[0]
747
576
  : cwd || process.cwd();
748
- if (process.argv[2] === "backup" || process.argv[2] === "backup-cursor" || process.argv[2] === "backup-copilot") {
577
+ if (process.argv[2] === "backup" || process.argv[2] === "backup-cursor" || process.argv[2] === "backup-copilot" || process.argv[2] === "backup-pi") {
749
578
  if (transcript_path && !(0, utils_1.isCodexRolloutPath)(transcript_path)) {
750
579
  try {
751
580
  (0, utils_1.dispatchCaptureFinish)({
@@ -753,7 +582,9 @@ async function main() {
753
582
  ? "capture-cursor"
754
583
  : process.argv[2] === "backup-copilot"
755
584
  ? "capture-copilot"
756
- : "capture",
585
+ : process.argv[2] === "backup-pi"
586
+ ? "capture-pi"
587
+ : "capture",
757
588
  session_id,
758
589
  transcript_path,
759
590
  cwd,
@@ -878,7 +709,10 @@ async function runCaptureFinishInner(input, config, session_id, transcript_path,
878
709
  transcriptContent = normalized.content;
879
710
  copilotSubagents = normalized.subagents.count > 0 ? normalized.subagents : undefined;
880
711
  }
881
- if (mode === "cursor" || mode === "copilot") {
712
+ else if (parserMode === "pi") {
713
+ transcriptContent = (0, piHooks_1.normalizePiTranscript)(transcriptContent);
714
+ }
715
+ if (mode === "cursor" || mode === "copilot" || mode === "pi") {
882
716
  try {
883
717
  await (0, limitEventReporter_1.reportTranscriptLimitEvent)({ config, source: mode, sessionId: session_id, transcript: transcriptContent });
884
718
  }
@@ -902,7 +736,9 @@ async function runCaptureFinishInner(input, config, session_id, transcript_path,
902
736
  ? "empty-cursor-transcript"
903
737
  : parserMode === "copilot" && (0, copilotHooks_1.isCopilotTranscriptEmpty)(rawTranscriptContent)
904
738
  ? "empty-copilot-transcript"
905
- : null;
739
+ : parserMode === "pi" && (0, piHooks_1.isPiTranscriptEmpty)(rawTranscriptContent)
740
+ ? "empty-pi-transcript"
741
+ : null;
906
742
  if (emptyReason) {
907
743
  (0, captureLog_1.appendCaptureLog)({
908
744
  stage: "capture-finish",
@@ -76,7 +76,7 @@ function postLimitEvent(config, payload, timeoutMs = 5_000) {
76
76
  async function reportTranscriptLimitEvent(args) {
77
77
  const parse = args.source === "codex"
78
78
  ? limitEvent_1.parseCodexLimitEventFromTranscript
79
- : args.source === "cursor" || args.source === "copilot" ? limitEvent_1.parseNativeLimitEventFromTranscript : limitEvent_1.parseLimitEventFromTranscript;
79
+ : args.source === "cursor" || args.source === "copilot" || args.source === "pi" ? limitEvent_1.parseNativeLimitEventFromTranscript : limitEvent_1.parseLimitEventFromTranscript;
80
80
  const parsed = parse(args.transcript);
81
81
  if (!parsed && !args.emitMinimalOnMiss)
82
82
  return false;
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ManagedPiArgError = exports.GUARD_SOURCE = exports.BLOCKED_BASE_URL = exports.CAPTURE_FILE = exports.GUARD_FILE = exports.PI_PROVIDER_ENV = exports.PI_PROVIDER_ID = void 0;
37
+ exports.managedPiAgentDir = managedPiAgentDir;
38
+ exports.piProviderConfig = piProviderConfig;
39
+ exports.prepareManagedPiDir = prepareManagedPiDir;
40
+ exports.managedPiEnv = managedPiEnv;
41
+ exports.managedPiArgs = managedPiArgs;
42
+ exports.runManagedPi = runManagedPi;
43
+ const child_process_1 = require("child_process");
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const codieLauncher_1 = require("./codieLauncher");
47
+ const piRuntime_1 = require("./piRuntime");
48
+ const utils_1 = require("./utils");
49
+ exports.PI_PROVIDER_ID = "codiedev";
50
+ exports.PI_PROVIDER_ENV = [
51
+ "ANTHROPIC_API_KEY", "ANTHROPIC_OAUTH_TOKEN", "OPENAI_API_KEY", "AZURE_OPENAI_API_KEY",
52
+ "AZURE_OPENAI_BASE_URL", "AZURE_OPENAI_RESOURCE_NAME", "DEEPSEEK_API_KEY", "GEMINI_API_KEY",
53
+ "GROQ_API_KEY", "CEREBRAS_API_KEY", "XAI_API_KEY", "FIREWORKS_API_KEY", "OPENROUTER_API_KEY",
54
+ "AI_GATEWAY_API_KEY", "ZAI_API_KEY", "MISTRAL_API_KEY", "MINIMAX_API_KEY", "MOONSHOT_API_KEY",
55
+ "OPENCODE_API_KEY", "KIMI_API_KEY", "CLOUDFLARE_API_KEY", "XIAOMI_API_KEY",
56
+ "XIAOMI_TOKEN_PLAN_CN_API_KEY", "XIAOMI_TOKEN_PLAN_AMS_API_KEY", "XIAOMI_TOKEN_PLAN_SGP_API_KEY",
57
+ "AWS_PROFILE", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN",
58
+ "AWS_BEARER_TOKEN_BEDROCK", "GOOGLE_APPLICATION_CREDENTIALS", "GOOGLE_CLOUD_API_KEY",
59
+ "COPILOT_GITHUB_TOKEN",
60
+ ];
61
+ const BLOCKED_SUBCOMMANDS = new Set(["install", "remove", "uninstall", "config"]);
62
+ exports.GUARD_FILE = "codie-guard.ts";
63
+ exports.CAPTURE_FILE = "codiedev-capture.ts";
64
+ exports.BLOCKED_BASE_URL = "http://127.0.0.1:9/blocked-by-codiedev";
65
+ exports.GUARD_SOURCE = `// Written by Codiedev on every \`codie --pi\` launch. Edits are overwritten.
66
+ import { execFileSync } from "node:child_process";
67
+ import { getProviders } from "@mariozechner/pi-ai";
68
+
69
+ const PROVIDER = ${JSON.stringify(exports.PI_PROVIDER_ID)};
70
+ const BLOCKED = ${JSON.stringify(exports.BLOCKED_BASE_URL)};
71
+ const ours = (m) => Boolean(m && m.provider === PROVIDER);
72
+ const label = (m) => (m ? m.provider + "/" + m.id : "that model");
73
+ // pi runs a header value starting with "!" as a shell command, so no value
74
+ // may start with one; branch and skill names are user-controlled.
75
+ const clean = (v, max) => String(v ?? "").replace(/[\\r\\n]/g, "").trim().replace(/^!+/, "").slice(0, max);
76
+ const git = (cwd, args) => { try { return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 1500 }).trim(); } catch { return ""; } };
77
+
78
+ export default function (pi) {
79
+ for (const p of getProviders()) if (p !== PROVIDER) pi.registerProvider(p, { baseUrl: BLOCKED });
80
+
81
+ let last;
82
+ const skills = new Set();
83
+ const fallback = (ctx) => last ?? ctx.modelRegistry.getAll().find(ours);
84
+ const pin = async (model, ctx) => {
85
+ const back = fallback(ctx);
86
+ if (back) await pi.setModel(back);
87
+ if (ctx.hasUI) ctx.ui.notify("codie: " + label(model) + " isn't a Codiedev-approved model" + (back ? "; using " + back.id : "") + ".", "warning");
88
+ };
89
+ const tag = (ctx) => {
90
+ const headers = {};
91
+ const session = clean(ctx.sessionManager?.getSessionId?.(), 200);
92
+ if (session) headers["X-Codiedev-Session-Id"] = session;
93
+ const repo = clean(git(ctx.cwd, ["remote", "get-url", "origin"]).replace(/\\/\\/[^/@]*@/, "//"), 2048);
94
+ if (repo) headers["X-Codiedev-Repo-Url"] = repo;
95
+ const branch = clean(git(ctx.cwd, ["branch", "--show-current"]), 200);
96
+ if (branch) headers["X-Codiedev-Branch"] = branch;
97
+ const sha = clean(git(ctx.cwd, ["rev-parse", "HEAD"]), 40);
98
+ if (/^[0-9a-f]{7,40}$/i.test(sha)) headers["X-Codiedev-Head-Sha"] = sha;
99
+ const list = clean([...skills].join(","), 4096);
100
+ if (list) headers["X-Codiedev-Skills-Used"] = list;
101
+ // pi replaces the provider's whole request config here, so the key name
102
+ // goes with the headers every time.
103
+ pi.registerProvider(PROVIDER, { apiKey: "CODIEDEV_GATEWAY_KEY", headers });
104
+ };
105
+
106
+ pi.on("session_start", async (_event, ctx) => {
107
+ skills.clear();
108
+ for (const m of ctx.modelRegistry.getAll()) if (!ours(m)) pi.registerProvider(m.provider, { baseUrl: BLOCKED });
109
+ if (ours(ctx.model)) last = ctx.model;
110
+ else await pin(ctx.model, ctx);
111
+ tag(ctx);
112
+ });
113
+ pi.on("model_select", async (event, ctx) => {
114
+ if (ours(event.model)) last = event.model;
115
+ else await pin(event.model, ctx);
116
+ });
117
+ pi.on("input", async (event) => {
118
+ const m = String(event?.text ?? "").match(/^\\/skill:([a-z0-9][a-z0-9_.:-]{0,119})(?:\\s|$)/i);
119
+ if (m && skills.size < 50) skills.add(m[1]);
120
+ });
121
+ pi.on("tool_result", async (event) => {
122
+ const p = String(event?.input?.path ?? "").replace(/\\\\/g, "/").match(/\\/([a-z0-9][a-z0-9_.:-]{0,119})\\/SKILL\\.md$/i);
123
+ if (event?.toolName === "read" && !event.isError && p && skills.size < 50) skills.add(p[1]);
124
+ });
125
+ // Before every model call in the turn: the agent may have switched branch.
126
+ pi.on("turn_start", async (_event, ctx) => tag(ctx));
127
+ pi.on("before_provider_request", (_event, ctx) => {
128
+ // Belt and braces: the request is already headed for BLOCKED.
129
+ if (!ours(ctx.model)) ctx.abort();
130
+ });
131
+ }
132
+ `;
133
+ function managedPiAgentDir(stateDir) {
134
+ return path.join(stateDir, "managed", "pi", "agent");
135
+ }
136
+ function piProviderConfig(gatewayUrl, catalog) {
137
+ return {
138
+ baseUrl: gatewayUrl.replace(/\/+$/, ""),
139
+ api: "openai-completions",
140
+ apiKey: "CODIEDEV_GATEWAY_KEY",
141
+ compat: { supportsDeveloperRole: false, supportsReasoningEffort: false },
142
+ models: catalog.map((model) => ({
143
+ id: (0, codieLauncher_1.bareModel)(model.id),
144
+ name: model.name || (0, codieLauncher_1.bareModel)(model.id),
145
+ ...(model.limit ? { contextWindow: model.limit.context, maxTokens: model.limit.output } : {}),
146
+ ...(model.cost ? { cost: { input: model.cost.input, output: model.cost.output, cacheRead: 0, cacheWrite: 0 } } : {}),
147
+ })),
148
+ };
149
+ }
150
+ function writeJsonAtomic(file, value) {
151
+ const tmp = `${file}.${process.pid}.tmp`;
152
+ fs.writeFileSync(tmp, JSON.stringify(value, null, 2) + "\n", { mode: 0o600 });
153
+ fs.renameSync(tmp, file);
154
+ }
155
+ function prepareManagedPiDir(opts) {
156
+ const { agentDir, gatewayUrl, catalog } = opts;
157
+ if (!catalog.length)
158
+ throw new Error("No company-approved models were returned by Codiedev");
159
+ const ids = catalog.map((model) => (0, codieLauncher_1.bareModel)(model.id));
160
+ const requested = (0, codieLauncher_1.bareModel)(opts.model ?? "");
161
+ if (requested && !ids.includes(requested)) {
162
+ throw new Error(`Primary agent model is not approved for this Codiedev account: ${requested}`);
163
+ }
164
+ fs.mkdirSync(agentDir, { recursive: true, mode: 0o700 });
165
+ writeJsonAtomic(path.join(agentDir, "models.json"), { providers: { [exports.PI_PROVIDER_ID]: piProviderConfig(gatewayUrl, catalog) } });
166
+ writeJsonAtomic(path.join(agentDir, "settings.json"), {
167
+ defaultProvider: exports.PI_PROVIDER_ID,
168
+ defaultModel: requested || ids[0],
169
+ enabledModels: ids.map((id) => `${exports.PI_PROVIDER_ID}/${id}`),
170
+ packages: [],
171
+ extensions: [],
172
+ skills: opts.skillPaths ?? [],
173
+ enableInstallTelemetry: false,
174
+ });
175
+ fs.rmSync(path.join(agentDir, "auth.json"), { force: true });
176
+ fs.rmSync(path.join(agentDir, "extensions"), { recursive: true, force: true });
177
+ const guard = path.join(agentDir, exports.GUARD_FILE);
178
+ fs.writeFileSync(guard, exports.GUARD_SOURCE, { mode: 0o600 });
179
+ const capture = path.join(agentDir, exports.CAPTURE_FILE);
180
+ fs.writeFileSync(capture, (0, utils_1.piExtensionSource)(), { mode: 0o600 });
181
+ return [guard, capture];
182
+ }
183
+ function managedPiEnv(env, agentDir, key, platform = process.platform) {
184
+ const blocked = new Set([...exports.PI_PROVIDER_ENV, "PI_CODING_AGENT_DIR"]);
185
+ const out = {};
186
+ for (const [k, v] of Object.entries(env)) {
187
+ if (blocked.has(platform === "win32" ? k.toUpperCase() : k))
188
+ continue;
189
+ out[k] = v;
190
+ }
191
+ out.PI_CODING_AGENT_DIR = agentDir;
192
+ out.PI_TELEMETRY = "0";
193
+ out.CODIEDEV_GATEWAY_KEY = key;
194
+ return out;
195
+ }
196
+ class ManagedPiArgError extends Error {
197
+ }
198
+ exports.ManagedPiArgError = ManagedPiArgError;
199
+ function managedPiArgs(args, extensions) {
200
+ if (args.length && BLOCKED_SUBCOMMANDS.has(args[0])) {
201
+ throw new ManagedPiArgError(`\`pi ${args[0]}\` changes extension sources, which codie --pi manages. Ask your Codiedev admin to approve a package instead.`);
202
+ }
203
+ for (let i = 0; i < args.length; i++) {
204
+ const a = args[i];
205
+ if (a === "--")
206
+ break;
207
+ const [flag, inline] = a.startsWith("--") && a.includes("=") ? [a.slice(0, a.indexOf("=")), a.slice(a.indexOf("=") + 1)] : [a, undefined];
208
+ if (flag === "--api-key")
209
+ throw new ManagedPiArgError("--api-key isn't allowed in codie --pi: models come from Codiedev.");
210
+ if (flag === "-e" || flag === "--extension")
211
+ throw new ManagedPiArgError(`${flag} isn't allowed in codie --pi: extensions are managed by Codiedev.`);
212
+ if (flag === "--provider") {
213
+ const value = inline ?? args[i + 1];
214
+ if (value !== exports.PI_PROVIDER_ID)
215
+ throw new ManagedPiArgError(`--provider ${value ?? "(none)"} isn't allowed in codie --pi: only Codiedev-approved models are available.`);
216
+ }
217
+ }
218
+ return ["--no-extensions", ...extensions.flatMap((file) => ["-e", file]), ...args];
219
+ }
220
+ function runManagedPi(opts) {
221
+ const env = opts.env ?? process.env;
222
+ const agentDir = managedPiAgentDir(opts.stateDir);
223
+ const extensions = prepareManagedPiDir({
224
+ agentDir, gatewayUrl: opts.gatewayUrl, catalog: opts.catalog, model: env.CODIEDEV_MODEL,
225
+ skillPaths: opts.skillPaths,
226
+ });
227
+ const args = managedPiArgs(opts.args, extensions);
228
+ const bin = env.CODIEDEV_PI_BIN?.trim();
229
+ const [command, commandArgs] = bin
230
+ ? [bin, args]
231
+ : [process.execPath, [(opts.resolvePi ?? (() => (0, piRuntime_1.ensurePiRuntime)({ stateDir: opts.stateDir })))(), ...args]];
232
+ const result = (0, child_process_1.spawnSync)(command, commandArgs, {
233
+ stdio: "inherit",
234
+ env: managedPiEnv(env, agentDir, opts.key),
235
+ shell: Boolean(bin) && process.platform === "win32",
236
+ });
237
+ if (result.error)
238
+ throw result.error;
239
+ return result.status === null ? 1 : result.status;
240
+ }