ac-teams 0.0.14 → 0.0.15

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.
@@ -35,15 +35,21 @@ Other delegation tools remain outside these limits. See [companion tools](usage.
35
35
 
36
36
  `team_agent list` and `limits` include a fresh `resources` advisory. Launch and teammate resume results include the sample taken before startup.
37
37
 
38
- It reports host available RAM, occupied swap, recent memory stalls, and live/starting member counts. Values are bytes; Linux pressure averages are percentages over the last ten seconds. Missing or invalid metrics are `null` with a warning, not zero or an all-clear.
38
+ It reports host available RAM, occupied swap, recent memory stalls, and live/starting member counts. Values are bytes; Linux pressure averages are percentages.
39
+
40
+ `someAvg10` and `fullAvg10` cover ten-second trends; `fullAvg60` covers a sixty-second trend. Missing or invalid metrics are `null` with a warning, not zero or an all-clear.
39
41
 
40
42
  The check reads only `/proc/meminfo` and `/proc/pressure/memory` on demand. It adds no polling, process-tree scans, or subprocesses.
41
43
 
42
- Available RAM below 10% triggers a low-headroom warning (a heuristic, not a per-member budget). Any nonzero ten-second memory-stall average also warns. Occupied swap alone does not trigger a pressure warning.
44
+ Available RAM below 10% triggers a low-headroom warning (a heuristic, not a per-member budget). Low headroom alone does not prescribe sequential work.
45
+
46
+ Memory contention warns when `fullAvg60 >= 5` or `fullAvg10 >= 10`. Full pressure measures time when all non-idle work is stalled on memory. Partial pressure (`someAvg10`) remains informational; occupied swap alone does not trigger a pressure warning.
47
+
48
+ These thresholds are advisory defaults, not measured limits for optimal concurrency. Consider reducing concurrency if contention is slowing progress.
43
49
 
44
50
  Starting members reserve count slots synchronously, but may not yet consume their full memory. Concurrent launch results warn about earlier starts.
45
51
 
46
- Sequence launches and recheck `list` after startup before adding more. These advisories never block a launch, change the configured ceiling, reserve RAM, or stop a member. Host figures do not account for a tighter container/cgroup limit or guarantee future capacity; other teams and non-team processes also consume resources.
52
+ Choose concurrency using resource readings and observed progress. These advisories never block a launch, change the configured ceiling, reserve RAM, or stop a member. Host figures do not account for a tighter container/cgroup limit or guarantee future capacity; other teams and non-team processes also consume resources.
47
53
 
48
54
  Idle members keep Pi and its extensions alive. Stop unneeded members to release their owned processes, then resume their saved sessions when needed.
49
55
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ac-teams",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "description": "Small, persistent teams for Pi",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -356,7 +356,7 @@ export class Coordinator {
356
356
  const live = this.live();
357
357
  const startingMembers = live.filter(member => member.status === "starting").length;
358
358
  const warnings = [...sample.warnings];
359
- if (startingMembers) warnings.push(`${startingMembers} teammate(s) still starting; their full memory cost is not yet reflected. Sequence launches and recheck team_agent list after startup.`);
359
+ if (startingMembers) warnings.push(`${startingMembers} teammate(s) still starting; their full memory cost is not yet reflected in host headroom.`);
360
360
  return { ...sample, liveMembers: live.length, startingMembers, warnings };
361
361
  }
362
362
  ack(id: string, through: number): void {
package/src/index.ts CHANGED
@@ -280,7 +280,7 @@ export default function teams(pi: ExtensionAPI) {
280
280
  if (change) report(ctx, change);
281
281
  });
282
282
  function output(data: unknown) { return { content: [{ type: "text" as const, text: JSON.stringify(data) }], details: data }; }
283
- const guidance = "Team launches use real long-lived Pi sessions. Idle members count against the shared ceiling and retain their processes and extensions. Check team_agent list resources before adding concurrency; teammate launch/resume results include pre-launch host headroom. Resource warnings are advisory, not a memory reservation or capacity guarantee. Sequence launches while members are starting or memory is pressured; stop unneeded idle members and resume their saved sessions later. Use teammate names for messaging and stable IDs for member control and tasks; query status and history instead of acknowledging notifications. Limits cover only ac-teams, not other launchers or shell commands.";
283
+ const guidance = "Team launches use real long-lived Pi sessions. Idle members count against the shared ceiling and retain their processes and extensions. Check team_agent list resources before adding concurrency; teammate launch/resume results include pre-launch host headroom. Resource warnings are advisory, not a memory reservation or capacity guarantee. Choose concurrency using resource readings and observed progress, not low headroom alone. If contention slows progress, stop unneeded idle members and resume their saved sessions later. Use teammate names for messaging and stable IDs for member control and tasks; query status and history instead of acknowledging notifications. Limits cover only ac-teams, not other launchers or shell commands.";
284
284
  const taskTrackingGuidance = "Before delegating a deliverable through team_agent, create or reuse a team_task and include its ID in the member's brief. Use one shared task list for your own work and delegated work; do not create one task per teammate by default. Quick lookups and brief second opinions need no separate task unless their results need coordination or follow-up.";
285
285
  pi.registerTool({
286
286
  name: "team_agent", label: "Team agent", description: "Launch/resume Pi members, list/status, stop owned subtrees, change root limits, and explicitly clean stopped worktrees. Create or reuse team_task entries for delegated deliverables; quick lookups need no task unless results need coordination or follow-up.",
package/src/resources.ts CHANGED
@@ -3,8 +3,8 @@ import { readFileSync } from "node:fs";
3
3
  export interface HostResources {
4
4
  scope: "host";
5
5
  memory: { totalBytes: number; availableBytes: number; swapUsedBytes: number } | null;
6
- /** Percentage of wall time stalled on memory over the last ten seconds (Linux PSI). */
7
- memoryPressure: { someAvg10: number; fullAvg10: number } | null;
6
+ /** Linux PSI percentages: partial ten-second pressure and full ten-/sixty-second pressure. */
7
+ memoryPressure: { someAvg10: number; fullAvg10: number; fullAvg60: number } | null;
8
8
  warnings: string[];
9
9
  }
10
10
 
@@ -22,19 +22,19 @@ export function hostResources(read: (path: string) => string = path => readFileS
22
22
  const swapTotal = bytes("SwapTotal"), swapFree = bytes("SwapFree");
23
23
  if ([totalBytes, availableBytes, swapTotal, swapFree].every(Number.isSafeInteger) && totalBytes > 0 && availableBytes <= totalBytes && swapFree <= swapTotal) {
24
24
  result.memory = { totalBytes, availableBytes, swapUsedBytes: swapTotal - swapFree };
25
- if (availableBytes < totalBytes * 0.1) result.warnings.push("Host available memory is below 10%. Sequence work; stop unneeded idle members and resume their saved sessions later.");
25
+ if (availableBytes < totalBytes * 0.1) result.warnings.push("Host available memory is below 10%. This is low headroom, not a per-member capacity estimate.");
26
26
  }
27
27
  } catch { /* Missing or unreadable metrics remain explicitly unavailable. */ }
28
28
  try {
29
29
  const text = read("/proc/pressure/memory");
30
- const avg = (kind: string) => {
31
- const match = text.match(new RegExp(`^${kind} avg10=(\\d+(?:\\.\\d+)?) `, "m"));
30
+ const avg = (kind: string, window: 10 | 60) => {
31
+ const match = text.match(new RegExp(`^${kind} .*?\\bavg${window}=(\\d+(?:\\.\\d+)?) `, "m"));
32
32
  return match ? Number(match[1]) : NaN;
33
33
  };
34
- const someAvg10 = avg("some"), fullAvg10 = avg("full");
35
- if ([someAvg10, fullAvg10].every(value => Number.isFinite(value) && value >= 0 && value <= 100) && fullAvg10 <= someAvg10) {
36
- result.memoryPressure = { someAvg10, fullAvg10 };
37
- if (someAvg10 > 0) result.warnings.push(`Host tasks stalled on memory for ${someAvg10}% of the last ten seconds. Prefer sequential work over more teammates.`);
34
+ const someAvg10 = avg("some", 10), fullAvg10 = avg("full", 10), fullAvg60 = avg("full", 60);
35
+ if ([someAvg10, fullAvg10, fullAvg60].every(value => Number.isFinite(value) && value >= 0 && value <= 100) && fullAvg10 <= someAvg10) {
36
+ result.memoryPressure = { someAvg10, fullAvg10, fullAvg60 };
37
+ if (fullAvg60 >= 5 || fullAvg10 >= 10) result.warnings.push(`Significant memory contention detected (full avg10: ${fullAvg10}%, full avg60: ${fullAvg60}%). Consider reducing concurrency if progress is slowing.`);
38
38
  }
39
39
  } catch { /* PSI is optional on older kernels. */ }
40
40
  }